在此处使用协变类型在协变位置上会导致哪些违规行为?
考虑一个协变类型为 T
的接口。我正在研究以下情况:该接口的所有使用 T
的派生类中的属性均为只读,如果是通用类则为协变。假设此接口然后定义了一个使用 T
作为参数类型的方法。
Consider an interface with a covariant type T
. I'm examining the case where properties in all derived classes of this interface that use T
are readonly, and covariant if a generic class. Suppose this interface then defines a method that uses T
as an argument type. What violations does it permit?
例如,请考虑:
interface ICov<out T> {
void maybe_safe_set(T v);
}
class ImplCov<T> : ICov<T> {
public readonly T a;
public readonly IEnumerable<T> b;
public readonly IEnumerable<IEnumerable<T>> c;
// public readonly IList<T> d; // but not this
public void maybe_safe_set(T v) {
// do things that can't modify state: the type of our
// readonly, covariant IEnumerable members can't be modified
}
}
在C#中,我得到错误:
In C#, I get the error:
无效的方差:类型参数 T必须在 ConsoleApplication.ICov.maybe_safe_set(T)上相反地有效。 'T'是协变的。
Invalid variance: The type parameter 'T' must be contravariantly valid on 'ConsoleApplication.ICov.maybe_safe_set(T)'. 'T' is covariant.
这并不奇怪,因为 T
是处于相反位置。但是,我无法想到这里会发生违规情况。
which is not surprising, since T
is found in a contravariant position. However, I can't think of a violation that can occur here.
您:
interface ICov<out T> // BAD!
{
void maybe_safe_set(T v);
}
这是问题所在。像往常一样,我们有:
Here comes the problem. As usual we have:
class Animal { /* ... */ }
class Dog : Animal { public void Woof() { } /* ... */ }
class Cat : Animal { /* ... */ }
然后考虑:
class Impl : ICov<Dog>
{
public void maybe_safe_set(Dog v)
{
v.Woof(); // our 'Dog' v can really bark
}
}
然后这样:
var impl1 = new Impl();
ICov<Dog> impl2 = impl1; // OK, implements that
ICov<Animal> impl3 = impl2; // OK, you claim interface is covariant ('out')!
var badAnimal = new Cat();
impl3.maybe_safe_set(badAnimal); // ICov<Animal> takes in Animal, right?
// oh my God, you mad a 'Cat' bark!
当人们询问协方差和协变时,这总是一个相同的例子。
It is always the same example when people ask about co- and contravariance.