<out T> vs <T> в Generics

189

В чем разница между <out T>и <T>? Например:

public interface IExample<out T>
{
    ...
}

против

public interface IExample<T>
{
    ...
}
Коул Джонсон
источник
1
Хорошим примером могут быть IObservable <T> и IObserver <T>, определенные в системе ns в mscorlib. открытый интерфейс IObservable <out T> и открытый интерфейс IObserver <in T>. Аналогично, IEnumerator <out T>, IEnumerable <out T>
VivekDev,
2
Лучшее объяснение, которое я встретил: agirlamonggeeks.com/2019/05/29/… . (<В T> <- означает, что T может быть передано только в качестве параметра методу; <out T> <- означает, что T может быть возвращается только как результат метода)
Владимир Шарый

Ответы:

212

outКлючевое слово в воспроизведенных используется для обозначения того, что тип Т в интерфейсе ковариантен. Смотрите Ковариантность и Контравариантность для деталей.

Классический пример IEnumerable<out T>. Поскольку IEnumerable<out T>он ковариантен, вы можете делать следующее:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Вторая строка выше потерпит неудачу, если это не будет ковариантно, хотя логически это должно работать, так как строка происходит от объекта. До того, как дисперсия универсальных интерфейсов была добавлена ​​в C # и VB.NET (в .NET 4 с VS 2010), это была ошибка времени компиляции.

После .NET 4 IEnumerable<T>был отмечен ковариант и стал IEnumerable<out T>. Поскольку он IEnumerable<out T>использует только элементы внутри него и никогда не добавляет / не изменяет их, для него безопасно обрабатывать перечисляемую коллекцию строк как перечислимую коллекцию объектов, что означает ее ковариантность .

Это не будет работать с подобным типом IList<T>, так как IList<T>имеет Addметод. Предположим, это будет разрешено:

IList<string> strings = new List<string>();
IList<object> objects = strings;  // NOTE: Fails at compile time

Вы могли бы тогда позвонить:

objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object

Это, конечно, потерпит неудачу - поэтому IList<T>не может быть отмечено ковариантным.

Между прочим, существует также опция для in- которая используется такими вещами, как интерфейсы сравнения. IComparer<in T>Например, работает наоборот. Вы можете использовать бетон IComparer<Foo>непосредственно как подкласс IComparer<Bar>if , потому что интерфейс контравариантен .BarFooIComparer<in T>

Рид Копси
источник
4
@ColeJohnson Потому Imageчто это абстрактный класс;) Вы можете делать new List<object>() { Image.FromFile("test.jpg") };без проблем, или вы можете делать то new List<object>() { new Bitmap("test.jpg") };же самое . Проблема с твоим заключается в том, что new Image()это не разрешено (ты тоже не можешь этого сделать var img = new Image();)
Рид Копси
4
дженерик IList<object>- причудливый пример, если вы хотите objects, вам не нужны дженерики.
Джодрелл
5
@ ReedCopsey Разве ты не противоречишь своему собственному ответу в своем комментарии?
MarioDS
64

Для удобства запоминания использования inи outключевого слова (а также ковариации и контравариантности) мы можем использовать наследование изображения в виде переноса:

String : Object
Bar : Foo

в / из

o0omycomputero0o
источник
11
Разве это не неправильно? Contravariance = in = позволяет использовать менее производные типы вместо более производных. / Covariance = out = позволяет использовать больше производных типов вместо менее производных. Лично, глядя на вашу диаграмму, я читаю это как противоположность этому.
Сэм Шайлс
совместно U вариант (: для меня
ОСШ
49

рассматривать,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

и функции,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

Функция, которая принимает, ICovariantSkinned<Fruit>будет в состоянии принять ICovariantSkinned<Fruit>или ICovariantSkinned<Bananna>потому что ICovariantSkinned<T>является ковариантным интерфейсом и Bananaтипом Fruit,

функция, которая принимает, ISkinned<Fruit>сможет только принять ISkinned<Fruit>.

Jodrell
источник
38

« out T» означает, что тип Tявляется «ковариантным». Это ограничивает Tотображение только как возвращенное (исходящее) значение в методах универсального класса, интерфейса или метода. Подразумевается, что вы можете привести тип / интерфейс / метод к эквиваленту с супер-типом T.
Например, ICovariant<out Dog>можно привести к ICovariant<Animal>.

Мир джеймса
источник
14
Я не понимал, что outпринудительные меры, которые Tмогут быть возвращены только, пока я не прочитал этот ответ. Вся концепция имеет больше смысла сейчас!
MarioDS
6

По ссылке вы разместили ....

Для параметров универсального типа ключевое слово out указывает, что параметр типа ковариантен .

РЕДАКТИРОВАТЬ : опять же по ссылке, которую вы разместили

Для получения дополнительной информации см. Ковариантность и Контравариантность (C # и Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx

Брэд Каннингем
источник