2

我尝试了以下方法,结果在接口名称中:

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

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

文档有这样的说法:

C# 中的refout参数不能变体。

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

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

4

2 回答 2

1

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

Eric Lippert 的一个相关的很好的答案(但还有更多):

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

于 2014-09-24T23:39:04.000 回答
0

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

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

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

我认为从概念上讲它应该可以工作,因为通过使用 out 参数修饰符,我们只希望将传递的变量作为输出。如果编译器有一个特殊的规则,它就会起作用,这样当它遇到上述情况时,它应该认为强制转换是安全的并且不会产生错误。

但是我认为 C# 设计师权衡了利弊,最终决定保持一致的类型检查规则,这通常是不允许的向下转换。

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

于 2014-09-19T15:24:14.523 回答