.NET 4.0 Ковариантность - PullRequest
       10

.NET 4.0 Ковариантность

9 голосов
/ 21 февраля 2012

В ответ на другой вопрос я попытался сделать следующее. Я не думаю, что правильно истолковал этот вопрос, но мне интересно, возможно ли как-то нижеприведенное (мои попытки не увенчались успехом), и если нет, то почему:

    public class MyBaseClass { }

    public class MyClass : MyBaseClass { }

    public class B<T> { }

    public class A<T> : B<T> { }

    static void Main(string[] args)
    {
        // Does not compile
        B<MyBaseClass> myVar = new A<MyClass>();
    }

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

    interface IB<out T> { }

    public class B<T> : IB<T> { }

но я ошибался, это тоже не работает.

EDIT

Как указывал SLaks «Интерфейсы ковариантны; классы нет. (спасибо SLaks). Итак, теперь мой вопрос почему? Какова была идея дизайна (одна для Эрика Липперта, я думаю), это невозможно, нежелательно, или это в списке «возможно, один день»?

Ответы [ 3 ]

27 голосов
/ 21 февраля 2012

Что задумывалось за проектным решением не реализовывать дисперсию в типовых типовых классах? Разве это невозможно, нежелательно или входит в список «возможно, один день»?

Вы в хорошей компании; Джон Скит и Билл Вагнер задали мне тот же вопрос пару недель назад. Я работал над сообщением в блоге, но кратко:

Правильный человек, который попросит дать окончательный ответ, - Эндрю Кеннеди из Microsoft Research Cambridge, который первоначально разработал и внедрил большую часть общей логики и логики отклонений. Тем не менее, я могу подвергнуть сомнению обоснованное предположение о том, почему мы решили отказаться от различий в общих классах.

Короткая версия: Безопасная ковариация T требует, чтобы операции над T были «только для чтения». Контравариантность T требует, чтобы операции над T были «только для записи». У вас есть класс C<T>, который вы хотите изменить в T. Предположим, `C имеет поле типа T: вы хотите, чтобы это поле было только для чтения или только для записи ? Потому что это ваш выбор!

При каких обстоятельствах даже смутно полезно иметь поле, в которое можно записывать, но не читать? Не много. При каких обстоятельствах полезно иметь поле, которое может быть прочитано, но не записано? Только если поле помечено только для чтения .

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

Я большой поклонник неизменяемых типов данных, и я думаю, что было бы замечательно иметь возможность создавать ковариантный неизменяемый стек без использования интерфейса. Но ковариантные универсальные постоянные неизменяемые функциональные структуры данных еще не совсем распространены в C #, и они, конечно, не были такими, когда генерики были добавлены в CLR. Более того, у нас нет вспомогательной инфраструктуры, кроме полей только для чтения, чтобы выразить понятие «это неизменяемый тип данных» в C # или CLR; если бы мы делали ковариантные типы классов для неизменяемых классов, было бы неплохо сделать лотов функций, которые поддерживают неизменяемые классы, а не только этот неясный.

Итак, я вижу, как эта функция не выполняла разрез в CLR 2.0 / C # 2.0. Если бы мы проектировали его снова сегодня, когда программирование в функциональном стиле несколько более популярно, возможно, так и было бы. Но у нас нет планов делать это в ближайшее время.

В ближайшие несколько месяцев я напишу сообщение в блоге, в котором будет дан более подробный ответ.

4 голосов
/ 21 февраля 2012

Интерфейсы ковариантны; классы нет.

A<MyClass> можно преобразовать в IB<MyBaseClass>.

3 голосов
/ 21 февраля 2012

Непосредственное преобразование класса в класс не работает, но если вы используете интерфейс IB для типа переменной, то все в порядке. В соответствии с этим сообщением блога MSDN о ковариации FAQ и страницей MSDN о ковариантности и контравариантности , параметры типа варианта ограничены универсальным интерфейсом и универсальными типами делегатов.

public class MyBaseClass  {}

public class MyClass : MyBaseClass {}

interface IB<out T>{}

public class B<T> : IB<T> {}

public class A<T> : B<T> {}

static void Main(string[] args)
{
    IB<MyBaseClass> myVar = new A<MyClass>();
}
...