Take the following:
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) {}
}
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:
Why do ref and out parameters not allow type variation?
See the blog page for more commentary on this issue. Thanks for the great question.
=============
Let's suppose you have classes
Animal
,Mammal
,Reptile
,Giraffe
,Turtle
andTiger
, with the obvious subclassing relationships.Now suppose you have a method
void M(ref Mammal m)
.M
can both read and writem
.No. That variable could contain a
Turtle
, butM
will assume that it contains only Mammals. ATurtle
is not aMammal
.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.)No.
M
can write tom
, andM
might want to write aTiger
intom
. Now you've put aTiger
into a variable which is actually of typeGiraffe
.Conclusion 2:
ref
parameters cannot be made "smaller".Now consider
N(out Mammal n)
.No.
N
can write ton
, andN
might want to write aTiger
.Conclusion 3:
out
parameters cannot be made "smaller".Hmm.
Well, why not?
N
cannot read fromn
, it can only write to it, right? You write aTiger
to a variable of typeAnimal
and you're all set, right?Wrong. The rule is not "
N
can only write ton
".The rules are, briefly:
1)
N
has to write ton
beforeN
returns normally. (IfN
throws, all bets are off.)2)
N
has to write something ton
before it reads something fromn
.That permits this sequence of events:
x
of typeAnimal
.x
as anout
parameter toN
.N
writes aTiger
inton
, which is an alias forx
.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.
Conclusion 4:
out
parameters cannot be made "larger".Final conclusion: Neither
ref
norout
parameters may vary their types. To do otherwise is to break verifiable type safety.If these issues in basic type theory interest you, consider reading my series on how covariance and contravariance work in C# 4.0.