为什么C#不支持变体泛型类?

Ken*_*rey 19 c# generics class generic-variance

拿这个小LINQPad示例:

void Main()
{
    Foo<object> foo = new Foo<string>();
    Console.WriteLine(foo.Get());
}

class Foo<out T>
{
    public T Get()
    {
        return default(T);
    }
}
Run Code Online (Sandbox Code Playgroud)

它无法使用此错误进行编译:

方差修饰符无效.只能将接口和委托类型参数指定为变量.

我没有看到代码的任何逻辑问题.一切都可以静态验证.为什么不允许这样做?它是否会导致语言不一致,或者由于CLR的限制而被认为实施起来太昂贵了?如果是后者,我作为开发人员应该知道什么是上述限制?

考虑到接口支持它,我希望从逻辑上遵循该类支持.

Jon*_*nna 10

一个原因是:

class Foo<out T>
{
  T _store;
  public T Get()
  {
    _store = default(T);
    return _store;
  }
}
Run Code Online (Sandbox Code Playgroud)

此类包含一个不协变的功能,因为它有一个字段,字段可以设置为值.它虽然以协变的方式使用,因为它只被分配了默认值,而且只能null用于实际使用协方差的任何情况.

因此,目前尚不清楚我们是否可以允许它.不允许它会激怒用户(它毕竟符合你建议的相同潜在规则),但允许它很难(分析已经变得有点棘手了,我们甚至没有开始寻找真正棘手的案例).

另一方面,对此的分析要简单得多:

void Main()
{
  IFoo<object> foo = new Foo<string>();
  Console.WriteLine(foo.Get());
}

interface IFoo<out T>
{
  T Get();
}

class Foo<T> : IFoo<T>
{
  T _store;
  public T Get()
  {
    _store = default(T);
    return _store;
  }
}
Run Code Online (Sandbox Code Playgroud)

很容易确定没有任何实现IFoo<T>打破协方差,因为它没有任何.所有必要的是确保不使用T作为参数(包括setter方法的参数)并完成它.

由于类似的原因,潜在限制在类上比在接口上更加艰巨,这也降低了协变类有用的程度.它们当然不会毫无用处,但它们对于指定和实施关于它们将被允许做什么的规则的工作量的有用程度的平衡要远远小于协变接口的有用性的平衡.关于如何指定和实施它们的工作量.

当然,差异足以让它超越"好吧,如果你要允许X,那么不允许Y ......就是愚蠢的".