Разница между интерфейсом как ограничение типа и интерфейсом как параметр? - PullRequest
13 голосов
/ 25 октября 2011

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

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

Ответы [ 5 ]

11 голосов
/ 25 октября 2011

Пара отличий:

  • Если вам когда-нибудь снова понадобится тип real (например, для перехода к другому универсальному методу или для ведения журнала), тогда универсальный метод будет лучше
  • T может быть типом значения и все равно в конечном итоге будет распакован в общей форме. Маловероятно, что это будет иметь место для IList, но для других интерфейсов это весьма вероятно
  • Общая форма позволяет вам указать конкретный тип реализации, который отличается от фактического типа объекта. Например, вы можете передать пустую ссылку, но все же хотите знать, какой тип реализации T равен
  • Они будут по-разному ДРОЖЕНЫ; см. недавний блог Джо Даффи о дженериках для получения дополнительной информации

Конечно, это действительно зависит от того, что вы делаете ... если только вам на самом деле не нужно ничего знать о T в методе, единственным преимуществом для универсальной формы является точка бокса.

2 голосов
/ 25 октября 2011

если Foo2 возвращает void, это не имеет значения.Но предположим, что Foo2 вернул измененную версию списка.С параметром IList лучшее, что он может сделать, это вернуть другой IList.Но с ограничением IList он может возвращать любой тип, который хочет вызывающий, при условии, что этот тип реализует IList

0 голосов
/ 25 октября 2011

Простой:

здесь вы привели довольно простой пример: так как вы уже дали верхний интерфейс ILIST.

но

Допустим, у меня есть экземпляр объектов.

JimMorrison
JohnLennon
SnowieWhite

они все из ILegends.

все они - объекты певцов (var singer = new Singer ())

YokOno тоже певец, но не Ilegends (мммм ... интересно, почему?)

и ваша функция может занять Singer.

но вы хотите убедиться, что только ILegeneds будут действительны ... так что здесь ваш ответ.

0 голосов
/ 25 октября 2011

С учетом вашего примера я бы предпочел второй подход.Универсальные методы или классы чаще всего используются, когда вызывающая сторона может использовать их не только с типом, как в вашем примере.В этом случае вы можете избежать использования базового класса (например, object) в качестве возвращаемого значения и предоставить безопасные возвращаемые значения типа.На простом образце

public class Foo<T>
{
    public T GetSomething()
    {
        return default(T);
    }
}
0 голосов
/ 25 октября 2011

Помимо всех последствий нижнего уровня, когда список инкапсулирован и существуют операции для его изменения, вы говорите об объекте, и этот список не должен быть представлен (в большинстве случаев), поэтому использование дженерики бессмысленны и без веской причины разоблачают внутреннюю работу класса.

Если у вас есть структура данных, которая должна представлять список, то указание ее с помощью обобщений, как правило, облегчает чтение данных (ИМХО).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...