这个协方差相关代码背后的意义是什么?
What is the point behind this covariance related code?
我一直在读一本名为 c# 7.0 in a Nutshell by O'REILLY 的书,主题:方差不是自动的。有一个例子有 2 classes,Animal 和 Bear,其中 Animal>Bear:
public class Animal { }
public class Bear: Animal { }
还有一个像这样的class:
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];
}
继续有两个相同的版本class:
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
。但是第二行是正确的并且工作正常。由于我是这个 topc 的新手,我无法理解这两行之间的差异,我认为它们都接受 Stack<Animal>
为什么我们需要使用条件泛型?
Stack<Animal>
表示任何 Animal
类型的对象堆栈。
Stack<T> where T: Animal
表示 单一 类型的堆栈,只要该类型继承自 Animal
.
您不能使用 Stack<Bear>
代替声明为 Stack<Animal>
的参数,因为如果您 可以 ,则该方法可以推送 Fish
放到熊堆上。当使用 Bear
s 堆栈的方法将它从堆栈中弹出时,想象一下它弹出一条鱼时的惊喜!
另一方面,第二种方法是 generic,这意味着它可以接受任何类型的堆栈,只要该类型继承自 Animal
因此,如果该方法获得 Stack<Bear>
,它可以 仅 将另一个 Bear
压入堆栈。尝试推送 Fish
会出现运行时错误。
我不会称之为 "covariance"。 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
给它,但是不能再给ti加上Animal
s:
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>
,对吗?
我一直在读一本名为 c# 7.0 in a Nutshell by O'REILLY 的书,主题:方差不是自动的。有一个例子有 2 classes,Animal 和 Bear,其中 Animal>Bear:
public class Animal { }
public class Bear: Animal { }
还有一个像这样的class:
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];
}
继续有两个相同的版本class:
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
。但是第二行是正确的并且工作正常。由于我是这个 topc 的新手,我无法理解这两行之间的差异,我认为它们都接受 Stack<Animal>
为什么我们需要使用条件泛型?
Stack<Animal>
表示任何 Animal
类型的对象堆栈。
Stack<T> where T: Animal
表示 单一 类型的堆栈,只要该类型继承自 Animal
.
您不能使用 Stack<Bear>
代替声明为 Stack<Animal>
的参数,因为如果您 可以 ,则该方法可以推送 Fish
放到熊堆上。当使用 Bear
s 堆栈的方法将它从堆栈中弹出时,想象一下它弹出一条鱼时的惊喜!
另一方面,第二种方法是 generic,这意味着它可以接受任何类型的堆栈,只要该类型继承自 Animal
因此,如果该方法获得 Stack<Bear>
,它可以 仅 将另一个 Bear
压入堆栈。尝试推送 Fish
会出现运行时错误。
我不会称之为 "covariance"。 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
给它,但是不能再给ti加上Animal
s:
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>
,对吗?