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



我尝试了以下操作,结果显示在接口名称中:

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

文档是这样说的:

refout参数在c#中是不能变的。

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

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

我的问题实际上已经在c#的ref和out参数中有了答案,不能标记为变体

Eric Lippert的精彩回答的相关部分(但还有更多):

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

如果对协方差使用'out'参数修饰符,编译器可能会认为需要进行不安全的类型转换。

请看这个场景。假设有一个方法期望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

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

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

然而,我认为c#设计者权衡了利弊,最终决定维护一个一致的类型检查规则,这通常是不允许向下转换的。

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

最新更新