c# 4.0 -不能让co/ contr- variance在c#中工作

本文关键字:contr- variance 工作 co 不能 | 更新日期: 2023-09-27 17:49:26

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();
  }
}

不能确定条件表达式的类型,因为在` ConsoleApplication4. 4 `之间没有隐式转换。E' and 'ConsoleApplication4.C'

现在我得到了"为什么"(我认为),但我看不出如何使这个编译。我认为我必须创建一个接口来定义某种类型的方差并继承它,但我不确定。但无论如何,E()应该继承C()

人吗?

TIA

c# 4.0 -不能让co/ contr- variance在c#中工作

要在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(); }

一个长颈鹿集合现在包含了一个包含老虎的动物列表。

如果你要使用方差,你必须是类型安全的。因为编译器不能确定方差注释是类型安全的,所以它拒绝了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

完美的类型安全。这是一个合法的c# 4程序。

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

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance +和+抗变性/

请注意,这些是按照从最近到最近的顺序列出的;从下面开始。

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

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

EC之间没有关系。因此,您的返回类型必须是A<B>,它可以被视为这些类型的共同父类型。此外,A需要是一个接口,其中用out定义了泛型参数,以使其工作:

interface A<out T> { }
class B { }
class C : A<B> { }
class D : B { }
class E : A<D> { }
static class X
{
    public static A<B> GetThing(bool f)
    {
        if (f)
        {
            return new E();
        }
        return new C();
    }
}

不能使用三元运算符(?:)。编译器就是不喜欢它。


更新:

在@Eric Lippert在评论部分的精彩评论之后,您可以通过强制转换到A<B>来使用三元操作符:

public static A<B> GetThing(bool f)
{
    return f ? (A<B>)new E() : new C();
}

这不能工作,因为CE没有共同的父节点。要做到这一点,您需要创建一个公共父类型,无论是类还是接口。