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