==============
更新:我将此答案用作此博客条目的基础:
为什么 ref 和 out 参数不允许类型变化?
有关此问题的更多评论,请参阅博客页面。谢谢你的好问题。
==============
假设您有类Animal, Mammal, Reptile,和Giraffe,具有明显的子类关系。TurtleTiger
现在假设你有一个方法void M(ref Mammal m)。 M可以读写m。
您可以将类型的变量传递Animal给M吗?
不,该变量可以包含 a Turtle,但M会假定它仅包含哺乳动物。ATurtle不是Mammal.
结论 1:ref参数不能“更大”。(动物比哺乳动物多,所以变量变得“更大”,因为它可以包含更多的东西。)
您可以将类型的变量传递Giraffe给M吗?
号M可以写到,m并且M可能要写到。现在您已将 a放入一个实际上是 type 的变量中。TigermTigerGiraffe
结论2:ref参数不能“更小”。
现在考虑N(out Mammal n)。
您可以将类型的变量传递Giraffe给N吗?
号N可以写n,并且N可能想写一个Tiger。
结论3:out参数不能“更小”。
您可以将类型的变量传递Animal给N吗?
唔。
那么,为什么不呢? N无法读取n,只能写入,对吗?您将 a 写入一个Tiger类型的变量,Animal然后一切就绪,对吗?
错误的。规则不是“N只能写到n”。
规则是,简要地说:
1)必须在正常返回之前N写入。(如果抛出,所有赌注都关闭。)nNN
2)N必须先写一些东西才能n从n.
这允许以下事件序列:
- 声明一个
x类型的字段Animal。
x作为out参数传递给N.
N将 aTiger写入n,这是 的别名x。
- 在另一个线程上,有人将 a
Turtle写入x.
N尝试读取 的内容,并在它认为是类型的变量中n发现 a 。TurtleMammal
显然,我们希望将其设为非法。
结论4:out参数不能“大”。
最终结论:参数和参数都不能改变它们的类型。否则会破坏可验证的类型安全。refout
如果您对基本类型理论中的这些问题感兴趣,请考虑阅读我关于 C# 4.0 中协变和逆变如何工作的系列文章。