问题描述
采取以下措施:
class A {}
class B : A {}
class C
{
C()
{
var b = new B();
Foo(b);
Foo2(ref b); // <= compile-time error:
// "The 'ref' argument doesn't match the parameter type"
}
void Foo(A a) {}
void Foo2(ref A a) {}
}
为什么会出现上述编译时错误?ref
和 out
参数都会发生这种情况.
Why does the above compile-time error occur? This happens with both ref
and out
arguments.
推荐答案
=============
=============
更新:我将此答案用作此博客条目的基础:
UPDATE: I used this answer as the basis for this blog entry:
为什么 ref 和 out 参数不允许类型变化?
有关此问题的更多评论,请参阅博客页面.谢谢你的好问题.
See the blog page for more commentary on this issue. Thanks for the great question.
=============
=============
假设你有类 Animal
、Mammal
、Reptile
、Giraffe
、Turtle
和 Tiger
,具有明显的子类关系.
Let's suppose you have classes Animal
, Mammal
, Reptile
, Giraffe
, Turtle
and Tiger
, with the obvious subclassing relationships.
现在假设你有一个方法void M(ref Mammal m)
.M
可以读写m
.
Now suppose you have a method void M(ref Mammal m)
. M
can both read and write m
.
你能把 Animal
类型的变量传递给 M
吗?
Can you pass a variable of type
Animal
toM
?
没有.该变量可能包含 Turtle
,但 M
将假定它仅包含哺乳动物.乌龟
不是哺乳动物
.
No. That variable could contain a Turtle
, but M
will assume that it contains only Mammals. A Turtle
is not a Mammal
.
结论1:ref
参数不能变大".(动物比哺乳动物多,因此变量变得更大",因为它可以包含更多的东西.)
Conclusion 1: ref
parameters cannot be made "bigger". (There are more animals than mammals, so the variable is getting "bigger" because it can contain more things.)
你能把 Giraffe
类型的变量传递给 M
吗?
Can you pass a variable of type
Giraffe
toM
?
没有.M
可以写入 m
,而 M
可能想要将 Tiger
写入 m
.现在您已将 Tiger
放入一个实际上是 Giraffe
类型的变量中.
No. M
can write to m
, and M
might want to write a Tiger
into m
. Now you've put a Tiger
into a variable which is actually of type Giraffe
.
结论2:ref
参数不能变小".
现在考虑 N(out Mammal n)
.
你能把 Giraffe
类型的变量传递给 N
吗?
Can you pass a variable of type
Giraffe
toN
?
没有.N
可以写入 n
,而 N
可能想要写入 Tiger
.
No. N
can write to n
, and N
might want to write a Tiger
.
结论3:out
参数不能变小".
你能把 Animal
类型的变量传递给 N
吗?
Can you pass a variable of type
Animal
toN
?
嗯.
好吧,为什么不呢?N
不能从 n
中读取,它只能写入它,对吧?你写一个 Tiger
到一个 Animal
类型的变量,你就准备好了,对吧?
Well, why not? N
cannot read from n
, it can only write to it, right? You write a Tiger
to a variable of type Animal
and you're all set, right?
错了.规则不是N
只能写入 n
".
Wrong. The rule is not "N
can only write to n
".
简单来说,规则是:
1) N
必须在N
正常返回之前写入n
.(如果 N
抛出,所有赌注都关闭.)
1) N
has to write to n
before N
returns normally. (If N
throws, all bets are off.)
2) N
必须先向 n
写入内容,然后才能从 n
读取内容.
2) N
has to write something to n
before it reads something from n
.
这允许这样的事件序列:
That permits this sequence of events:
- 声明
Animal
类型的字段x
. - 将
x
作为out
参数传递给N
. N
将Tiger
写入n
,它是x
的别名.- 在另一个线程上,有人将
Turtle
写入x
. N
尝试读取n
的内容,并在它认为是Mammal 类型的变量中发现了一个
Turtle
.
- Declare a field
x
of typeAnimal
. - Pass
x
as anout
parameter toN
. N
writes aTiger
inton
, which is an alias forx
.- On another thread, someone writes a
Turtle
intox
. N
attempts to read the contents ofn
, and discovers aTurtle
in what it thinks is a variable of typeMammal
.
显然我们想让它成为非法的.
Clearly we want to make that illegal.
结论4:out
参数不能变大".
最终结论:ref
和out
参数都不能改变它们的类型.否则会破坏可验证的类型安全性.
Final conclusion: Neither ref
nor out
parameters may vary their types. To do otherwise is to break verifiable type safety.
如果您对基本类型理论中的这些问题感兴趣,请考虑阅读 我关于协变和逆变如何在 C# 4.0 中工作的系列文章.
If these issues in basic type theory interest you, consider reading my series on how covariance and contravariance work in C# 4.0.
这篇关于为什么'ref'和'out'不支持多态性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!