在此处使用协变类型在协变位置上会导致哪些违规行为?

问题描述:

考虑一个协变类型为 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.