无法在C#中使用co/contra-variance

ena*_*ash 0 c# covariance contravariance c#-4.0

abstract class A<T> {
  List<T> Items { get; set; }
}

class B {}
class C : A<B> {}

class D : B {}
class E : A<D> {}

static class X {
  public A<B> GetThing(bool f) {
    return f ? new E() : new C();
  }
}
Run Code Online (Sandbox Code Playgroud)

无法确定条件表达式的类型,因为"ConsoleApplication4.E"和"ConsoleApplication4.C"之间没有隐式转换

现在我得到了"为什么"(我认为),但我看不出如何编译.我想我必须创建一个定义某种方差的接口并从中继承,但我不确定.但无论如何,E()都应该继承C().

任何接受者?

TIA

Eri*_*ert 15

要在C#中使用泛型差异,您必须满足以下所有条件:

  1. 使用C#4
  2. 变化的类型必须是通用接口或通用委托
  3. 类型参数必须标记为"in"(逆变)或"out"(协变)
  4. 类型参数注释必须产生一种类型,该类型在其所有可能的操作中都是可证明类型安全的
  5. "source"类型参数和"destination"类型参数必须在它们之间具有标识或引用转换.

您的程序符合条件1和条件5,但不符合条件2,3或4.

没有办法得到你想要的变化,因为你想要一些违反条件4的东西.观察当我们满足除条件4之外的所有条件时会发生什么:

// Suppose this declaration were legal:
interface IMyCollection<out T> 
{
  List<T> Items { get; set; } 
}

class Animal {}
class AnimalCollection : IMyCollection<Animal> 
{ 
  public List<Animal> { get; set; }
}

class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe> 
{
  public List<Giraffe> { get; set; } 
}

static class X 
{
  public IMyCollection<Animal> GetThing() 
  {
    // IMyCollection is covariant in T, so this is legal.
    return new GiraffeCollection(); 
  }
}

class Tiger : Animal {}

...

IMyCollection<Animal> animals = X.GetThing(); 
// GetThing() actually returns a GiraffeCollection
animals.Items = new List<Animal>() { new Tiger(); }
Run Code Online (Sandbox Code Playgroud)

长颈鹿系列现在包含一个包含老虎的动物列表.

如果你要使用方差,你必须是类型安全的.因为编译器无法将方差注释确定为类型安全,所以它拒绝IMyCollection的声明.

让我们看看我们如何做到这一点并保持类型安全.问题是Items可以通过界面写入.

interface IMyCollection<out T> 
{
  IEnumerable<T> Items { get; }
}

class Animal {}
class AnimalCollection : IMyCollection<Animal> 
{ 
  public IEnumerable<Animal> { get { yield return new Tiger(); } }
}

class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe> 
{
  public IEnumerable<Giraffe> { get { yield return new Giraffe(); } } 
}

static class X 
{
  public IMyCollection<Animal> GetThing() 
  {
    return new GiraffeCollection();
  }
}

class Tiger : Animal {}

...

MyCollection<Animal> animals = X.GetThing(); 
// GetThing() actually returns a GiraffeCollection
foreach(Animal animal in animals.Items) { ... } 
// Items yields a giraffe, which is an animal
Run Code Online (Sandbox Code Playgroud)

完全类型安全.这将是一个合法的C#4计划.

如果C#4中协方差和逆变的设计细节让您感兴趣,您可以考虑阅读我关于该主题的十几篇文章.你可以在这里找到它们:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

请注意,这些是按最近到最近的顺序列出的; 从底部开始.

如果您特别感兴趣的是确定接口注释何时有效的规则,请参阅本文(我刚刚发现并解决了一些错误,所以我很高兴我们进行了这次对话.)

http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx