С# понимание ковариационной структуры?

  • Предполагая

    класс A   {}

    класс B: A   {}

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

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

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

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

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

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

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

Я видел много примеров класса "конвертер" как форма Stack.

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

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

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

enter image description here

enter image description here

Мой вопрос:

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

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

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

Ответы

Ответ 1

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

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

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?" Ответ "вы не можете", потому что животное не может быть тигром! Если вы хотите проверить, действительно ли ссылка на Animal является тигром, скажите:

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

или

if (myAnimal is Tiger) ...

Как насчет того, хотите ли вы преобразовать класс C<B> в C<A>?

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

Ответ 2

Похоже, вы используете конвертер просто для получения ссылки типа 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);
    }
}

Ответ 3

IPushable<B> x1 = new MyConverter<B>();
x1.Set(b);
// I believe this is valid.
IPoppable<A> final = x2;

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