您的代码可以编译并且可以工作,那么它“正确”吗?我想是的!
但是,只有一个元素的堆栈并不是很有趣;那不是真正的堆栈。让我们考虑一下如何制作真正的协变和逆变堆栈。
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
如果我只想从 A 的引用中使用 B 的一个实例怎么办?
您的问题是“如果我想使用 Tiger 但我有一个 Animal 参考怎么办?”答案是“你不能”,因为动物可能不是老虎!如果你想测试对 Animal 的引用是否真的是老虎,那么说:
Tiger tiger = myAnimal as Tiger;
if (tiger != null) ...
或
if (myAnimal is Tiger) ...
如果你想把C<B>类转换成C<A>呢?
这是不可能的。那里没有参考转换。 C# 4 中唯一的协变和逆变引用转换是在泛型接口和泛型委托上,使用引用类型作为类型参数构造。泛型类和结构不能协变或逆变使用。你能做的最好的事情就是让类实现一个变体接口。