<out T> vs <T> в Generics

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

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

против

public interface IExample<T>
{
    ...
}

Ответ 1

Ключевое слово out в generics используется для обозначения того, что тип T в интерфейсе ковариантен. Подробнее см. Ковариация и контравариантность.

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

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

Вторая строка выше завершится неудачно, если это не будет ковариантным, хотя логически это должно работать, поскольку строка происходит от объекта. До дисперсия в общих интерфейсах была добавлена ​​в С# и 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> не может быть помечен как ковариантный.

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

Ответ 2

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

String : Object
Bar : Foo

in/out

Ответ 3

рассмотреть,

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>.

Ответ 4

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

Ответ 5

Из опубликованной вами ссылки....

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

ИЗМЕНИТЬ: Опять же, из ссылки, которую вы разместили

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