为什么C#不支持变体泛型类? [重复]

时间:2021-06-07 21:00:47

This question already has an answer here:

这个问题在这里已有答案:

Take this small LINQPad example:

拿这个小LINQPad示例:

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

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

It fails to compile with this error:

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

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

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

I don't see any logical problem with the code. Everything can be statically verified. Why is this not allowed? Would it cause some inconsistency in the language, or was it deemed too expensive to implement due to a limitation in the CLR? If it is the latter, what should I as a developer know about said limitation?

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

Considering that interfaces support it, I would have expected class support to logically follow from that.

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

2 个解决方案

#1


9  

One reason would be:

一个原因是:

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

This class contains a feature that is not covariant, because it has a field, and fields can be set to values. It is though used in a covariant way, because it is only ever assigned the default value and that is only ever going to be null for any case where covariance is actually used.

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

As such it's not clear if we could allow it. Not allowing it would irritate users (it does after all match the same potential rules you suggest), but allowing it is difficult (the analysis has gotten slightly tricky already and we're not that even beginning to hunt for really tricky cases).

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

On the other hand, the analysis of this is much simpler:

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

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

It's easy to determine that none of the implementation of IFoo<T> breaks the covariance, because it hasn't got any. All that's necessary is to make sure that there is no use of T as a parameter (including that of a setter method) and it's done.

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

The fact that the potential restriction is a lot more arduous on a class than on an interface for similar reasons, also reduces the degree to which covariant classes would be useful. They certainly wouldn't be useless, but the balance of how useful they would be over how much work it would be to specify and implement the rules about what they would be allowed to do is much less than the balance of how useful covariant interfaces are over how over how much work it was to specify and implement them.

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

Certainly, the difference is enough that it's past the point of "well, if you're going to allow X it would be silly to not allow Y…".

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

#2


0  

A class would need to contain only output method parameters (in order to be covariant) and only input method parameters (in order to be contravariant). The point is that it's hard to guarantee that for classes: for example, covariant class (by T type parameter) cannot have fields of T, because you could write to those fields. It would work great for truly immutable classes, but there is no a comprehensive support for immutability in C# at the moment (say, as in Scala).

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

#1


9  

One reason would be:

一个原因是:

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

This class contains a feature that is not covariant, because it has a field, and fields can be set to values. It is though used in a covariant way, because it is only ever assigned the default value and that is only ever going to be null for any case where covariance is actually used.

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

As such it's not clear if we could allow it. Not allowing it would irritate users (it does after all match the same potential rules you suggest), but allowing it is difficult (the analysis has gotten slightly tricky already and we're not that even beginning to hunt for really tricky cases).

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

On the other hand, the analysis of this is much simpler:

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

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

It's easy to determine that none of the implementation of IFoo<T> breaks the covariance, because it hasn't got any. All that's necessary is to make sure that there is no use of T as a parameter (including that of a setter method) and it's done.

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

The fact that the potential restriction is a lot more arduous on a class than on an interface for similar reasons, also reduces the degree to which covariant classes would be useful. They certainly wouldn't be useless, but the balance of how useful they would be over how much work it would be to specify and implement the rules about what they would be allowed to do is much less than the balance of how useful covariant interfaces are over how over how much work it was to specify and implement them.

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

Certainly, the difference is enough that it's past the point of "well, if you're going to allow X it would be silly to not allow Y…".

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

#2


0  

A class would need to contain only output method parameters (in order to be covariant) and only input method parameters (in order to be contravariant). The point is that it's hard to guarantee that for classes: for example, covariant class (by T type parameter) cannot have fields of T, because you could write to those fields. It would work great for truly immutable classes, but there is no a comprehensive support for immutability in C# at the moment (say, as in Scala).

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