Подтвердить что ты не робот

С# ковариантный общий параметр

Я пытаюсь понять это, но результаты поиска не получили.

В С# 4 я могу сделать

    public interface IFoo<out T>
    {

    }

Как это отличается от

    public interface IFoo<T>
    {

    }

Все, что я знаю, это out делает общий ковариантный параметр (??). Может ли кто-нибудь объяснить использование <out T> части с примером? А также почему применимо только для интерфейсов и делегатов, а не для классов?

Извините, если он дубликат и закроет его как таковой, если он есть.

4b9b3361

Ответ 1

Может кто-нибудь объяснить использование части T с примером?

Конечно. IEnumerable<T> является ковариантным. Это означает, что вы можете сделать это:

static void FeedAll(IEnumerable<Animal> animals) 
{
    foreach(Animal animal in animals) animal.Feed();
}

...

 IEnumerable<Giraffe> giraffes = GetABunchOfGiraffes();
 FeedAll(giraffes);

"Ковариант" означает, что отношение совместимости присваивания аргумента типа сохраняется в родовом типе. Giraffe - это присвоение, совместимое с Animal, и поэтому это отношение сохраняется в построенных типах: IEnumerable<Giraffe> - это присвоение, совместимое с IEnumerable<Animal>.

Почему применимо только для интерфейсов и делегатов, а не для классов?

Проблема с классами состоит в том, что классы имеют тенденцию иметь изменяемые поля. Возьмем пример. Предположим, что мы допустили это:

class C<out T>
{
    private T t;

Хорошо, теперь подумайте над этим вопросом, прежде чем продолжить. Может ли C<T> использовать какой-либо метод вне конструктора, который устанавливает поле t в значение, отличное от его значения по умолчанию?

Поскольку он должен быть typeafe, C<T> теперь не может иметь методов, которые принимают T как аргумент; T может быть возвращен только. Итак, кто устанавливает t, и где они получают значение, которое они устанавливают?

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

Я бы хотел, чтобы мы это сделали, но нам нужно жить с системой типа CLR, которую нам дали. Я надеюсь, что в будущем мы сможем лучше поддерживать как неизменные классы, так и ковариантные классы.

Если эта функция вас интересует, подумайте о том, чтобы прочитать мою длинную серию о том, как мы разработали и внедрили эту функцию. Начните снизу:

https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/

Ответ 2

Если мы говорим об общей дисперсии:

Ковариация - это все значения, возвращаемые из операции обратно вызывающему.

Контравариантность. Его противоположность, и о значениях, передаваемых вызывающим:

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

В классах нет дисперсии (ковариации или контравариантности), потому что даже если у вас есть класс, который использует только параметр типа для ввода (или использует его только для вывода), вы не могу указать модификаторы in или out. Только интерфейсы и делегаты могут иметь параметры типа варианта. Во-первых, CLR этого не позволяет. С концептуальной точки зрения интерфейсы представляют собой способ взглянуть на объект с определенной точки зрения, тогда как классы - это более фактические реализации.

Ответ 3

Это означает, что если у вас есть это:

class Parent { } 
class Child : Parent { }

Тогда экземпляр IFoo<Child> также является экземпляром IFoo<Parent>.