此协方差相关代码的意义是什么?

问题描述 投票:1回答:2

我一直在读一本名为c# 7.0 in a Nutshell by O'REILLY的书,主题:方差不是自动的。有一个示例有2个类,动物和熊,其中Animal> Bear:

public class Animal { }
public class Bear: Animal { }

还有一个这样的类:

public class Stack<T>
{
    private int position;
    T[] data = new T[100];
    public void Push(T obj) => data[position++] = obj;
    public T Pop() => data[--position];
}

继续,有2个相同类别的版本:

public class ZooCleaner1
{
    public static void Wash(Stack<Animal> animals) { }
}

和:

public class ZooCleaner2
{
    public static void Wash<T>(Stack<T> animals) where T: Animal { }
}

它解释说,如果我尝试写:

ZooCleaner1.Wash(bears);
ZooCleaner2.Wash(bears);

第一行出现编译时错误,表明它无法将Bear转换为Animal。但是第二行是正确的并且可以正常工作。由于我是本教程的新手,所以我无法理解这两行之间的区别,我认为它们都接受Stack<Animal>,为什么我们需要使用条件泛型?

c# generics covariance
2个回答
2
投票

Stack<Animal>表示任何Animal类型的对象的堆栈。Stack<T> where T: Animal表示single类型的堆栈,只要该类型继承自Animal

您不能使用Stack<Bear>代替声明为Stack<Animal>的参数,因为如果您[[could,则该方法可能会将Fish推入熊的堆栈。当使用Bear堆栈的方法将其从堆栈中弹出时,请想象一下当它从一条鱼中弹出时会感到惊讶!

另一方面,第二种方法是

generic

,这意味着它可以接受任何类型的堆栈,只要该类型是从Animal继承的,那么如果该方法获得Stack<Bear>,则它可以only将另一个Bear推入堆栈。尝试推送Fish将是运行时错误。

1
投票
我不会将其称为“协方差”。 This是通用方差。您的代码仅演示通用约束。

让我们看看在每种Wash方法中我们能做什么。在第一个Wash方法中,我们可以:

public static void Wash(Stack<Animal> animals) { animals.Push(new Animal()); Animal a = animals.Pop(); }

现在假设您有一个Stack<Bear> bears;,并且您想将其传递到第一个Wash中。如果编译器允许您这样做,您是否看到这将如何产生矛盾?您实际上无法将Animal添加到Stack<Bear>!但是就Wash而言,添加Animal完全可以,因为它只知道它可以接受Stack<Animal>

因此,Stack<Bear>不是Stack<Animal>的子类型,因为您不能将Animal添加到前者,但可以将其添加到后者。

在第二种Wash方法中,尽管可以将bears传递给它,但是您不能再将Animal添加到ti:

public static void Wash<T>(Stack<T> animals) where T: Animal { animals.Push(new Animal()); // error Animal a = animals.Pop(); }

因为编译器不确定Stack<T>是否为Stack<Animal>

可能是,但也可以是Stack<Bear>,或者是Stack<Unicorn>Stack<SomeOtherSubclassOfAnimal>,对吧?

© www.soinside.com 2019 - 2024. All rights reserved.