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

Why doesn't C# support variant generic classes?

以这个 LINQPad 小例子为例:

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

class Foo<out T>
{
    public T Get()
    {
        return default(T);
    }
}

编译失败并出现此错误:

Invalid variance modifier. Only interface and delegate type parameters can be specified as variant.

我没有发现代码有任何逻辑问题。一切都可以静态验证。为什么不允许这样做?它会导致语言中的一些不一致,还是由于 CLR 的限制而被认为实施起来过于昂贵?如果是后者,作为开发人员,我应该了解哪些限制?

考虑到接口支持它,我希望 class 支持从逻辑上遵循。

A class 需要只包含输出方法参数(为了协变)和输入方法参数(为了逆变)。关键是很难保证classes:例如,协变class(通过T类型参数)不能有T的字段,因为你可以write 到那些字段。它对于真正不可变的 classes 非常有用,但目前 C# 中没有对不可变性的全面支持(比如在 Scala 中)。

一个原因是:

class Foo<out T>
{
  T _store;
  public T Get()
  {
    _store = default(T);
    return _store;
  }
}

这个class包含一个非协变的特征,因为它有一个字段,字段可以设置为值。它虽然以协变的方式使用,因为它只被分配了默认值,并且对于实际使用协方差的任何情况,它只会是 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;
  }
}

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

由于类似的原因,class 上的潜在限制比接口上的限制要困难得多,这一事实也降低了协变 classes 的有用程度。它们当然不会是无用的,但是它们的有用程度与指定和实施允许它们做什么的规则的工作量之间的平衡远小于协变接口的有用程度之间的平衡关于指定和实施它们的工作量。

当然,差异已经超过了"well, if you're going to allow X it would be silly to not allow Y…"。