c # ковариантный универсальный параметр - PullRequest
32 голосов
/ 28 июня 2011

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

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

    public interface IFoo<out T>
    {

    }

Чем это отличается от

    public interface IFoo<T>
    {

    }

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

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

Ответы [ 3 ]

40 голосов
/ 28 июня 2011

Может ли кто-нибудь объяснить использование части 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 значение, отличное от значения по умолчанию?

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

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

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

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

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

5 голосов
/ 28 июня 2011

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

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

Contravariance Это противоположно, и речь идет о значениях, передаваемых вызывающим:

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

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

3 голосов
/ 28 июня 2011

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

class Parent { } 
class Child : Parent { }

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

...