Разница между интерфейсом как ограничение типа и интерфейс как параметр?
Если бы я хотел создать метод, который принимает экземпляр IList
как параметр (или любой другой интерфейс, но пусть использует IList
в качестве примера), я мог бы создать общий метод с ограничением типа, например
public static void Foo1<T>(T list) where T : IList
{
}
В качестве альтернативы я мог бы создать метод, который принимает параметр IList
напрямую:
public static void Foo2(IList list)
{
}
Во всех смыслах и целях кажется, что эти методы ведут себя точно так же:
List<string> myList = new List<string>();
Foo1(myList);
Foo2(myList);
Итак, вот мой вопрос - какая разница между этими двумя подходами? Похоже, второй подход немного читаем; есть ли какие-либо другие различия, о которых я должен знать (разные ИЛ, генерируемые и т.д.)? Спасибо заранее.
Ответы
Ответ 1
Пара различий:
- Если вам когда-нибудь понадобится реальный тип (например, для перехода к другому универсальному методу или для ведения журнала), тогда общий метод будет лучше
-
T
может быть типом значения и все равно в конечном итоге распаковывается в общей форме. Это маловероятно, что это будет иметь место для IList
, но для других интерфейсов это очень правдоподобно
- Общая форма позволяет указать конкретный тип реализации, который отличается от фактического типа объекта. Например, вы можете передать нулевую ссылку, но все же хотите знать, какой тип реализации
T
есть
- Они будут JITted по-разному; см. Joe Duffy последнее сообщение в блоге о дженериках для получения дополнительной информации
Это действительно зависит от того, что вы делаете, конечно... если вам действительно не нужно ничего знать о T
внутри метода, единственным преимуществом для общей формы является точка бокса.
Ответ 2
если Foo2 возвращает void, это не имеет большого значения. Но предположим, что Foo2 вернул измененную версию списка. С параметром IList лучшее, что он может сделать, это вернуть другой IList. Но с ограничением IList он может возвращать любой тип, который вызывающий хочет предположить, что тип реализует IList
Ответ 3
Помимо всех последствий нижнего уровня, когда список инкапсулирован и есть операции для его изменения, вы говорите об объекте, и этот список не должен отображаться (в большинстве случаев), поэтому использование дженерики бессмысленны и без уважительной причины раскрывают внутреннюю работу класса.
Если у вас есть структура данных, которая должна раскрывать список, то указание его на generics обычно облегчает чтение данных (IMHO).
Ответ 4
Повторяя ваш пример, я предпочел бы второй подход. Общие методы или классы в основном используются, когда вызывающий может использовать их с более чем по типу, как в вашем примере. В этом случае вы можете избежать базового класса (например, object
) в качестве возвращаемого значения и предоставить безопасные возвращаемые значения типа.
На простой выборке
public class Foo<T>
{
public T GetSomething()
{
return default(T);
}
}
Ответ 5
Простой:
здесь вы дали довольно простой пример: поскольку вы уже дали верхний интерфейс ILIST.
но
говорит, что у меня есть instanc Of Objects.
JimMorrison
JohnLennon
SnowieWhite
все они из ILegends
.
все они - объекты певцов (var singer = new Singer())
YokOno
также является певцом, но не ILegends
(мммм... интересно, почему?)
и ваша функция может принимать Singer
.
но вы хотите убедиться, что только ILegeneds будут действительны...
поэтому здесь - ваш ответ.