public class B : A { }
public void Foo (ref A bar) { }
public void Baz ()
{
B b = new B ();
Foo (ref b);
}
The issue is hat you can't pass a 'B' parameter type by ref where a 'ref A' is expected. Why is this? What case could possibly fail if this was allowed?
11 comments:
public void Foo (ref A bar)
{
bar = new A();
}
If you call Foo with an B-Instance, it's like:
B b = new A();
And that is no allowed?!
I'm not sure, maybe I'm talking bullshit. :|
I think the anonymous poster there has it right. You could conceivably assign a base class to a derived class pointer. Such way lies madness and exceptions and ow.
Hah, that's a very good point. Now why did neither of us think of that before I blogged the question!
Try this:
B b = new B();
A a = (A)b;
Foo(ref a);
Should work.
I guess that the problem is because the compiler is treating ref types as invariant types, instead of covariant as you - and we all - want.
But I'm just guessing here...
The point to ref and out is that the called method can change the object reference, as the first anonymous poster pointed out was possible.
The ramifications, though:
// C is a sibling to B
class C : A {}
public void Bar(ref A bar)
{
b = new C();
// should be valid, as C is a
// subclass of A.
}
thus allowing the bizarro (and invalid):
public void Qux()
{
B b = new B();
Bar (ref b);
}
If the above were possible, a 'B' instance variable would in fact be referencing a type that cannot be a 'B' -- a 'C'!
This is, of course, bad.
It is for similar reasons that (without covariant/contravariant support) an IEnumerable[string] isn't implicitly convertable to an IEnumerable[object], and an IList[string] will never be implicitly convertable to an IList[object].
Basically, the same reason behind the invariant types on 2.0 Generics.
Great explanation jonmpryor!
public void Baz ()
{
B b = new B ();
Foo (ref b);
}
This wont work as Foo() requires an A-type. But as B is a child of A you can try:
public void Baz ()
{
A b = new B ();
Foo (ref b);
}
This will work and you don't need any casts
* A normal (in) parameter allows arguments from subclasses, but more importantly, disallows superclass arguments
* A return "parameter" allows assignment to "arguments" from superclasses, but more importantly, disallows assignment to subclass "arguments"
* An out parameter is like a return value, and theoretically (not in C#) can accept arguments from superclasses
* A ref parameter is in/out and thus allows neither subclasses nor superclasses
So, your question could've been: why doesn't C# allow superclasses in out parameters?
* firstly, it introduces complications in the overload resolution mechanism for relatively low benefit
* secondly, there's no way to distinguish, in a type-safe manner, between ref and out parameters at the CIL level. Yes, you can attach an [Out] attribute, but this doesn't enforce type-safety.
Fantastic post.I like your article.Very informative post.
Post a Comment