Ковариация против дисперсии

У меня есть небольшие сомнения в отношении CoVariance и ContraVariance .. См. Следующий код ..

interface IGetElement<out T>
{
    int Counter { get; }
    T GetNext();
}

interface IAddElement<in T>
{
    void Add(T t);
}

class Collection<T> : IAddElement<T>, IGetElement<T> where T : Fruit
{
    List<T> tList = new List<T>();

    private int _counter = 0;

    public int Count { get { return tList.Count; } }

    public void Add(T t)
    {
        tList.Add(t);
    }

    int IGetElement<T>.Counter
    {
        get { return _counter; }
    }

    public T GetNext()
    {
        return tList[_counter++];
    }

    public void Rset()
    {
        _counter = 0;
    }
}

abstract class Fruit
{
    public abstract void Taste();
}

class Apple : Fruit
{
    public override void Taste()
    {
        Console.WriteLine("Like Apple");
    }
}

Это пример кода .. И теперь Клиент

 static void Main(string[] args)
 {
        IGetElement<Fruit> covarience = new Collection<Apple>(); // CoVarience..

        IAddElement<Apple> contravarience = new Collection<Fruit>();//ContraVarience.. Compiling fine and working also fine... :)

        IGetElement<Fruit> fruits = new Collection<Apple>();

        IAddElement<Fruit> apples2 = fruits as IAddElement<Apple>;//Here its Compiler error : Cannot implicitly convert type 'Test.IAddElement<Test.Apple>' to 'Test.IAddElement<Test.Fruit>'. An explicit conversion exists (are you missing a cast?)  

        IAddElement<Apple> apples1 = fruits as IAddElement<Fruit>;//This is not posible  
        /* Why this is not possible..? In this case No Compiler Error But Run Time error ie., 
       apples1 is NULL.. I don't know why.. because.. in fruits cantains apple only but 
       It is unable it caste it as ContrVariantly..  ?------------1 */

        IAddElement<Apple> apples = fruits as IAddElement<Apple>;//This is posible 


        IGetElement<Fruit> f = apples as IGetElement<Apple>;//This is posible.. 
        /* Why this is posible.. ? here we are casting t as CoVariantly..
        If ------------1 is not posible how this would be posible... yes.. I am casting to    
        actual object which is not Null.. ? -----------2 */
  }

Пожалуйста, ответьте на мой вопрос в закомментированном исходном коде ... :) -------- 1, --------- 2.

Спасибо и привет, Динеш


person dinesh    schedule 15.09.2011    source источник


Ответы (2)


Чтобы немного расширить ответ Джеймса:

  1. Почему это невозможно? В этом случае ошибки компилятора нет, но во время выполнения apples1 имеет значение NULL. Не знаю почему.

Локальная переменная плодов во время выполнения относится к объекту типа Collection<Apple>. То есть коллекция, в которой только яблоки.

Вы спрашиваете: "Могу ли я добавить любые фрукты в эту коллекцию?" Нет. Вы можете добавлять только яблоки, но не фрукты. Следовательно, результат нулевой.

Причина, по которой это не удается во время выполнения, а не во время компиляции, заключается в том, что тип фруктов во время компиляции является типом интерфейса, и вы конвертируете его в другой тип интерфейса. Любые два интерфейса могут быть реализованы данным объектом; компилятор не выполняет анализ потока, чтобы определить, что фруктам всегда присваивается только один конкретный тип. Следовательно, проверка не может быть выполнена до времени выполнения.

  1. Почему это возможно? Здесь мы конвертируем ковариантно.

Здесь есть два преобразования. Сначала яблоки конвертируются в IGetElement<Apple>, а затем ковариантно конвертируются в IGetElement<Fruit>.

Первое преобразование выполнено успешно, поскольку локальная переменная apples ссылается на Collection<Apple>, который реализует IGetElement<Apple>. Вы спрашиваете: "Может ли этот предмет дать мне яблоко?" и ответ "да".

Второе преобразование проходит успешно, потому что компилятор знает, что «объект, который может передать мне яблоко», можно безопасно рассматривать как «объект, который может передать мне фрукт».

Теперь это ясно?

person Eric Lippert    schedule 15.09.2011

На №1 IAddElement<in T> контравариантен, в сообщает, что он совместим с типами, которые больше «ограничительный», т.е. более узкий. Поскольку вы как приводите Collection<Apple> к IAddElement<Fruit>, вы расширяете тип (делая его более общим), который является ковариацией, а не контравариантностью, поэтому во время выполнения это не удается, а null является возвращается из состава as.

Обратите внимание, что причина сбоя - это приведение as, а не контравариантное присвоение от IAddElement<Fruit> до IAddElement<Apple>, которое является контравариантным и законным. Но поскольку приведение as не работает, оно возвращает null, которое затем назначается.

На №2 фрукты - это Collection<Apple>, а яблоки по-прежнему относится к фруктам. . Когда вы приводите яблоки к IGetElement<Apple>, это работает, потому что Collection<Apple> отлично реализует IGetElement<Apple>.

Это ни ковариант, ни контравариант, потому что типы одинаковы (Apple). Затем это может быть присвоено IGetElement<Fruit>, которое является операцией расширения (ковариантной), и поскольку IGetElement<Fruit> отмечен как out, он поддерживает это.

Надеюсь это поможет!

person James Michael Hare    schedule 15.09.2011