Понимание ковариационной структуры C #? - PullRequest
15 голосов
/ 10 февраля 2012
  • Предположим,

    класс А {}

    класс B: A {}

ковариация не поддерживается для универсального класса.

Значение - мы не можем сделать что-то вроде этого:

MyConverter<B> x1= new MyConverter<B>();
MyConverter<A> x2= x1;  

Это прекрасно и понятно.

Из моего чтения - я понимаю, что Коварианс будет доступен:

«Если вы используете базовый базовый интерфейс, реализуемый в универсальном классе, - так что доступ к экземпляру объекта типа T будет доступен через эти интерфейсы».

У меня только одна проблема.

Я видел много примеров класса «преобразователь» в виде Stack.

Но никогда не понимал, «что если я хочу использовать только 1 экземпляр B из ссылки A?»

поэтому я попробовал код:

Создать B объект + значения ---> использовать универсальный конвертер для B ---> используйте ковариационный поток, чтобы получить его A эталон ---> теперь вы можете использовать его либо как А, либо как Б.

enter image description here

enter image description here

Мой вопрос:

Это правильный способ сделать это (использовать ковариацию только для 1 объекта)?

p.s. Код работает и скомпилирован нормально. http://i.stack.imgur.com/PJ6QO.png

В последнее время я много спрашивал / читал об этой теме - я углубляюсь в вещи, чтобы понять их как можно лучше.

Ответы [ 3 ]

16 голосов
/ 10 февраля 2012

Ваш код компилируется и работает, так что это "правильно"? Я думаю, это так!

Однако не очень интересно иметь стек, который содержит только один элемент; это не совсем стек. Давайте подумаем, как сделать действительно ковариантный и контравариантный стек.

interface IPush<in T> { void Push(T item); }
interface IPop<out T> { T Pop(); }
class Stack<T> : IPush<T>, IPop<T>
{
    private class Link
    {
        public T Item { get; private set; }
        public Link Next { get; private set; }
        public Link(T item, Link next) { this.Item = item; this.Next = next; }
    }

    private Link head;
    public Stack() { this.head = null; }

    public void Push(T item)
    {
        this.head = new Link(item, this.head);
    }

    public T Pop()
    {
        if (this.head == null) throw new InvalidOperationException();
        T value = this.head.Item;
        this.head = this.head.Next;
        return value;
    }
}

И теперь вы можете использовать стек ковариантно для выталкивания и, наоборот, для толкания:

Stack<Mammal> mammals = new Stack<Mammal>();
IPop<Animal> animals = mammals;
IPush<Giraffe> giraffes = mammals;
IPush<Tiger> tigers = mammals;
giraffes.Push(new Giraffe());
tigers.Push(new Tiger());
System.Console.WriteLine(animals.Pop()); // Tiger
System.Console.WriteLine(animals.Pop()); // Giraffe

Что если я хочу использовать только один экземпляр B из ссылки A?

Ваш вопрос: "что если я хочу использовать Тигра, но у меня есть ссылка на Животное?" Ответ «ты не можешь», потому что Животное не может быть Тигром! Если вы хотите проверить, действительно ли ссылка на Animal является тигром, скажите:

Tiger tiger = myAnimal as Tiger;
if (tiger != null) ...

или

if (myAnimal is Tiger) ...

А если вы хотите преобразовать класс C<B> в C<A>?

Это невозможно. Там нет ссылки преобразования там. Единственные ковариантные и контравариантные преобразования ссылок в C # 4 находятся на универсальных интерфейсах и универсальных делегатах , которые построены со ссылочными типами в качестве аргументов типа. Универсальные классы и структуры нельзя использовать ковариантно или контравариантно. Лучшее, что вы можете сделать, это заставить класс реализовать вариант интерфейса .

1 голос
/ 13 февраля 2012

Похоже, вы используете конвертер просто для получения ссылки типа A, указывающей на объект типа B. Есть намного более простой способ сделать это, называемый кастингом:

B b = new B();
A a = (A)b;

На самом деле, поскольку A является суперклассом B, преобразование неявное:

B b = new B();
A a = b;

Ваша программа может быть:

class Program
{
    static void Main(string[] args)
    {
        B b = new B { b1 = 22222 };
        A a = b;
        Console.WriteLine(a.a1);
        Console.WriteLine(((B)a).b1);
    }
}
0 голосов
/ 10 февраля 2012
IPushable<B> x1 = new MyConverter<B>();
x1.Set(b);
// I believe this is valid.
IPoppable<A> final = x2;

Вы можете найти несколько замечательных примеров и описаний этого в этом блоге .

...