Подтвердить что ты не робот

Разница между интерфейсом как ограничение типа и интерфейс как параметр?

Если бы я хотел создать метод, который принимает экземпляр 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);

Итак, вот мой вопрос - какая разница между этими двумя подходами? Похоже, второй подход немного читаем; есть ли какие-либо другие различия, о которых я должен знать (разные ИЛ, генерируемые и т.д.)? Спасибо заранее.

4b9b3361

Ответ 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 будут действительны... поэтому здесь - ваш ответ.