为什么我不能将输出参数与协变泛型类型一起使用?

时间:2014-07-03 04:22:33

标签: .net generics covariance output-parameter

我尝试了以下操作,结果在界面名称中:

interface NotOK<out T>
{
    bool TryDequeue(out T t);
}

interface OK<out T>
{
    T TryDequeue(out bool b);
}

The docs有这样的说法:

  C#中

ref out 参数不能改变。

为什么 ref 不能协变(或逆变,就此而言)是显而易见的,但为什么 out 参数不能协变,就像方法结果一样?

是编译器限制还是 out参数实际上打破了协方差约束?

2 个答案:

答案 0 :(得分:0)

这是因为如果您使用&#39; out&#39;编译器可能会认为需要进行不安全的类型转换。协方差的参数修饰符。

请参阅此方案。假设有一个方法f期望NotOK作为输入:

interface NotOK<out T>
{
    bool TryDequeue(out T t);
}

void f( NotOK<Animal> x)
{
   bool b ;
   Animal animal_in_f;
   b = x.TryDequeue(animal_in_f);
}

如果我有两个接口,请看看会发生什么:

NotOK<Animal> objA;
NotOK<Dog> objD;

使用objA作为f的输入,没问题。

f(objA);
// objA should have a method of signature bool TryDequeue(out Animal t)
// inside method f, it calls x.TryDequeue(animal_in_f); 
// where animal_in_f is Animal, type match

但如果允许协方差,则允许传递objD

f(objD);
// objD should have a method of signature bool TryDequeue(out Dog t)
// inside method f, it calls x.TryDequeue(animal_in_f); 
// where animal_in_f is Animal
// but this time x.TryDequeue is expecting a Dog!!
// It is unsafe to cast animal_in_f to Dog

所以你明白为什么不允许在协方差中使用。

我认为它在概念上应该可以工作,因为通过使用参数修饰符我们只想将传递的变量作为输出。如果编译器有一个特殊规则,它将起作用,这样当遇到上面的场景时,它应该认为转换是安全的,不会产生错误。

但是我认为C#设计师加权利弊,最后决定维持一致的类型检查规则,一般不允许向下转发。

在我看来,最好添加一个特殊规则,因为现在它限制了用法,比如它不能有一个方法返回两个需要使用out参数修饰符的T类型的对象。

答案 1 :(得分:0)

我的问题实际上已经在ref and out parameters in C# and cannot be marked as variant

得到了答案

相关位Eric Lippert's great answer(但还有更多):

  

不幸的是没有。 “out”实际上与幕后的“ref”没有什么不同。 “out”和“ref”之间的唯一区别是编译器禁止在被调用者分配之前从out参数读取,并且编译器在被调用者正常返回之前需要赋值。以C#以外的.NET语言编写此接口实现的人可以在初始化之前从该项读取,因此可以将其用作输入。因此,在这种情况下,我们禁止将T标记为“out”。这是令人遗憾的,但我们无能为力;我们必须遵守CLR的类型安全规则。