我尝试了以下操作,结果在界面名称中:
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参数实际上打破了协方差约束?
答案 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的类型安全规则。