Почему IEnumerable <T> определяется как IEnumerable <out T>, а не IEnumerable <T>

Возможный дубликат:
Почему IEnumerable <T> сделал ковариант в С# 4?

Я посмотрел MSDN для определения интерфейса IEnumerable<T> и посмотрел:

public interface IEnumerable<out T> : IEnumerable

Мне было интересно, почему T определяется как out, почему бы и нет?

public interface IEnumerable<T> : IEnumerable

В чем причина этого?

Ответы

Ответ 1

Более подробную информацию можно найти здесь.

out делает параметр типа ковариантным. То есть вы можете использовать либо тип, либо любые производные типы. Обратите внимание, что out работает только с дженериками, он имеет другое значение при использовании в сигнатурах методов (хотя вы, вероятно, уже знали это).

Вот пример, взятый с ссылка на страницу:

// Covariant interface. 
interface ICovariant<out R> { }

// Extending covariant interface. 
interface IExtCovariant<out R> : ICovariant<R> { }

// Implementing covariant interface. 
class Sample<R> : ICovariant<R> { }

class Program
{
    static void Test()
    {
        ICovariant<Object> iobj = new Sample<Object>();
        ICovariant<String> istr = new Sample<String>();

        // You can assign istr to iobj because 
        // the ICovariant interface is covariant.
        iobj = istr;
    }
}

Как вы можете видеть, out в сигнатуре интерфейса позволяет вы должны назначить ICovariant<String> переменной ICovariant<Object>, так как String происходит от Object. Без ключевого слова out вы не сможете сделать это, так как типы будут разными.

Подробнее о ковариации (и связанной с этим контравариантности) вы можете прочитать здесь.

Как указывали другие ответы, IEnumerable был только ковариантным в .NET 4. Попытка написать код, например:

IEnumerable<Object> strings = new List<string>();

будет компилироваться в .NET 4 и более поздних версиях, но не в предыдущих версиях.

Ответ 2

Covariance. Это позволяет сбору назначать элементы более определенного или производного типа, чем те, что указаны в его общем параметре.

IEnumerable<T> не всегда ковариантно; это было ново для .NET 4, и причина изменения объясняется здесь.

Ответ 3

Спецификатор параметра out обозначает ковариацию.

На практике

Если я определяю два интерфейса.

ISomeInterface<T>
{
}

ISomeCovariantInterface<out T> 
{
}

Затем я реализую их так.

SomeClass<T> : ISomeInterface<T>, ISomeCovariantInterface<T>
{
}

Затем я пытаюсь скомпилировать этот код,

ISomeCovariantInterface<object> = new SomeClass<string>(); // works
ISomeInterface<object> = new SomeClass<string>(); // fails

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

Ответ 4

Чтобы достичь этого:

class Base {}
class Derived : Base {}

List<Derived> list = new List<Derived>();
IEnumerable<Base> sequence = list;