Неявная контравариантность на классах - простой неудачный пример - PullRequest
1 голос
/ 02 февраля 2011

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

    public partial class WebForm1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        A a = new B();
        B b = new A();
    }
}

public class A
{
    int id { get; set; }
}
public class B : A
{
}

Установка a в B работает, что является ковариацией, но установка b для нового A завершается с ошибкой компиляции.Даже выполнение явного преобразования приводит к ошибке во время компиляции.Есть ли способ сделать это, или я просто совершенно неправильно понимаю контравариантность?

Ответы [ 3 ]

6 голосов
/ 03 февраля 2011

я просто совершенно не понимаю контравариантность?

Да. Вы совершенно и совершенно не поняли, что означают «ковариация» и «контравариантность» . Вы перепутали их с совместимостью назначений .

Это очень распространенная ошибка. Эти два понятия взаимосвязаны, но они совсем не совпадают .

Совместимость присваиваний - это свойство, что выражение одного типа может храниться в переменной другого типа.

Ковариация - это свойство, при котором сопоставление типов с типами сохраняет совместимость назначения направления. Если Giraffe совместим с присвоением с Animal, и это означает, что IEnumerable<Giraffe> совместимо с назначением с IEnumerable<Animal>, тогда отображение IEnumerable<T> является ковариантным .

См. Мою статью на эту тему для более подробной информации:

http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx

Работает установка a в B, что является ковариацией

Да, это работает. «a» совместимо с присвоением с B. Это не «ковариация», потому что ничего не меняется . Нет сопоставления типов с типами , которое сохраняет направление совместимости назначения, используемое в назначении "a = B"; нет ничего общего.

, но установка b на новый A завершается ошибкой компиляции.

Correct.

Есть ли способ сделать это?

Нет. Вместо «А» и «В» назовите их «Животное» и «Жираф». Каждый Жираф - Животное, поэтому если переменная может содержать Животное, то оно может содержать Жирафа. Если вы попытаетесь пойти другим путем, вы не сможете поместить Animal в переменную типа Giraffe. Животное на самом деле может быть тигром. Почему вам следует разрешить помещать тигра в переменную типа Giraffe?

5 голосов
/ 02 февраля 2011

Вы не можете сделать:

B b = new A();

Потому что A просто не a B.Назначение недействительно.Я не уверен, что я бы даже назвал эту дисперсию - это просто наследование.

В общем случае, когда у B есть члены, которых нет у A, вы можете видеть, что делать это бессмысленно (еслиb фактически содержит ссылку на объект A):

b.SomeMethod();

, где SomeMethod определен только в B, но эта логика распространяется на присваивание самой переменной.Даже если вы добавили приведение, оно имеет неявную проверку типа, которая может завершиться неудачей.

1 голос
/ 02 февраля 2011

Contravariance не о нарушении правил кастинга; это применимо в ситуации как:

void WithApple(Action<Apple> eat);

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

void EatFruit(Fruit fruit);

Поскольку он может есть любые фрукты, вы должны сказать:

WithApple(EatFruit);

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

Это отличается от ковариации, которая является несколько более интуитивным понятием:

void EatAllFruit(IEnumerable<Fruit> inBasket);

где вы должны быть в состоянии:

IEnumerable<Apple> apples = someBasket;
EatAllFruit(apples);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...