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

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


person Donut    schedule 25.10.2011    source источник


Ответы (5)


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

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

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

person Jon Skeet    schedule 25.10.2011
comment
Спасибо, что нашли время ответить. Я думал об этом из TMTOWTDI, так что какой путь лучше, тогда как ответ действительно это зависит. - person Donut; 25.10.2011
comment
К вашему сведению, ArraySegment<T> — это структура в Framework, которая реализует IList<T>. - person supercat; 21.08.2013

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

person Robert Levy    schedule 25.10.2011
comment
Я не думаю, что параметр универсального типа сам по себе будет определять возвращаемый тип метода. - person BoltClock; 25.10.2011
comment
нет, но это дает вам возможность сделать это... public static T Foo1<T>(T list) where T : IList - person Robert Levy; 25.10.2011
comment
@RobertLevy Хороший вопрос, я думал об этом гипотетически и не рассматривал вопрос о желании большей конкретики для возвращаемого типа. - person Donut; 25.10.2011

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

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

person viniciushana    schedule 25.10.2011

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

public class Foo<T>
{
    public T GetSomething()
    {
        return default(T);
    }
}
person Fischermaen    schedule 25.10.2011

Просто:

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

но ,

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

JimMorrison
JohnLennon
SnowieWhite

они все из ILegends.

все они являются объектами певцов ( var song = new Singer())

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

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

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

person Royi Namir    schedule 25.10.2011