为什么';tc#支持变量泛型类?

为什么';tc#支持变量泛型类?,c#,class,generics,generic-variance,C#,Class,Generics,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); } } void Main() { Foo-Foo=新的Foo(); Console.WriteL

以这个小LINQPad为例:

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

class Foo<out T>
{
    public T Get()
    {
        return default(T);
    }
}
void Main()
{
Foo-Foo=新的Foo();
Console.WriteLine(foo.Get());
}
福班
{
公共部门得不到
{
返回默认值(T);
}
}
无法编译,出现以下错误:

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

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


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

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

原因之一是:

class Foo<out T>
{
  T _store;
  public T Get()
  {
    _store = default(T);
    return _store;
  }
}
很容易确定
IFoo
的任何实现都不会破坏协方差,因为它没有任何协方差。所有必要的是确保没有使用
T
作为参数(包括setter方法的参数),并且已经完成

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


当然,差别已经足够大了,已经超过了“如果你要允许X,那么不允许Y是愚蠢的……”这一点。

@Stillgar:你能不能回答“由于底层CLR实现中的这一点,它不受支持”?@J。。。我当然看手册了。这仍然不能解释为什么它“需要做很多工作”。如果是因为实现细节,最好至少了解一点实现。@J..:也许你对如何和为什么不感兴趣,但这并不意味着每个人都必须无意识地遵循手册而不质疑任何设计决策。一般来说,对于“为什么X语言不支持Y?”这样的问题,最直接、最实用的答案是“因为没有人指定/实现它”。当然,更好的答案总是解释为什么它不像最初出现的那样好,如果有任何这样的解释,我建议阅读Erik Lippert的,答案:
答案总是一样的:因为从来没有人设计、指定、实现、测试、记录和发布过该功能。所有这六件事都是实现功能所必需的。所有这些都花费了大量的时间、精力和金钱。功能并不便宜,我们尽最大努力确保我们只提供给我们的用户带来最大可能好处的功能…
@J。。。那么为什么它对接口有意义呢?你反对“wild typecasting”的论点对我来说毫无意义。这很容易在编译时得到验证,就像我的例子一样。如果语言中没有深度的不变性支持,要保证这一点就不那么容易了。参见Eric Lippert关于a的优秀答案的链接。不一定需要不变性。例如,任何类型为T的公共字段都将违反规则,并且T必须是不变的。可能会有一些情况下,差异适用,但他们仍然可能存在。非常好的例子。我猜你的意思是,如果我的例子有效,那么人们会期望你的例子也有效?我知道这可能会给语言设计师带来一些麻烦。他们必须允许我的例子工作(这是一个更困难的分析,必须考虑访问级别等等),或者很好地解释为什么它没有。我们不允许的工作越多,编码人员实际使用这种差异进行编码的可能性就越小。正如我所说的,它肯定不是无用的(一开始在很多不可变的情况下,它确实是非常好的),但其成本效益肯定与变体接口的成本效益非常不同。
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;
  }
}