By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
444,190 Members | 1,653 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 444,190 IT Pros & Developers. It's quick & easy.

Strange asymmetric polymorphism when using out parameters. Bug?

P: n/a
Introduction:
As we all know, values of any class may be assigned to a reference of a
superclass.
This is simple polymorphism.
So if you have a class A and a class B that inherits from A you can write a
method that returns an instance of B and assign that to a reference of type
A. (Trivial)
Problem:
If you rewrite the method so that it no longer returns an instance of B as a
return value but as an out parameter we are in trouble!
The polymorphic rules that applied in the first case don't apply now for
some strange reason.
We get a compile error if we try to call the method with a reference of type
A.
Questions:
Is this a bug or is there a technical reason for this?
Perhaps it would be harder to resolve overloaded methods if you could do as
I want?
It seems to me like an out parameter actually is behaving more like a ref
parameter under the covers.
Example code below:
/Regards
Mårten Herberthson

class A{} //Class A
class B : A{} //Class B inherits from A (why am I telling you this ;-)

class Class1
{
//A method that returns an instance of a B as a return value
static B GetBAsReturnValue() {return new B();}
//A method that returns an instance of a B as an out parameter
static void GetBAsOutParameter(out B outB) {outB = new B();}

[STAThread]
static void Main(string[] args)
{
//The trivial case using a reference of type B
B b;
b = GetBAsReturnValue(); //works fine
GetBAsOutParameter(out b); //works fine

//The case that should be equally trivial but isn't.
//We use a variable of type A instead of B.
//This should be no problem since any reference to an A
//may be assigned values of type B
A a;
a = GetBAsReturnValue(); //works fine as expected.
GetBAsOutParameter(out a); //DOES NOT COMPILE!!!
}
}
Nov 15 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Marten,

While you are right in your comments about polymorphism, your assumption
that the two cases of returning a value through a parameter and the actual
return value are logically the same is incorrect.

The difference is that the routine can access the value passed into the
out parameter, while initially, there is no value in the return value (so to
speak).

So, they are different.

Basically, you are doing an end-run around type safety if you allow A to
be passed for B. The reason for this is that what if there is a class C
which extends A and in the method it assigns an instance of C to the output
parameter. Yes, it could be cast down to A, but what is the point of the
parameter being declared as B at that point? Type-safety goes out the
window. I wouldn't use a language that explicily ignored these things.

Hope this helps.
--
- Nicholas Paldino [.NET/C# MVP]
- ni**************@exisconsulting.com

"Mårten Herberthson" <he*********@hotmail.com> wrote in message
news:u5**************@TK2MSFTNGP11.phx.gbl...
Hi Selvin,

Thank you for your reply.

Obviously I could change the formal parameter to be of type A just to make
it compile, but my question is on a more fundamental level.
According to the rules of polymorphism it should be possible to do what I
want. Remember, when I return a B as a return value I can retrieve it in to an A so I should be able to do the same thing when *returning* it as an out parameter.

It is true what you write that "you could not cast (B)a 'cause A isn't
delvered from B", but since I am *returning* a B (out) there is no need to
cast the variable "a" to a B as it would have been if the parameter was ref. Again: It works then using a return value; it should work when using an out parameter. The two cases are logically the same.

/Regards

Mårten

"Selvin" <no***********@osadkowski.com.pl_nospam> wrote in message
news:bg**********@atlantis.news.tpi.pl...
class A{} //Class A
class B : A{} //Class B inherits from A (why am I telling you this ;-)
class Class1
{
//A method that returns an instance of a B as a return value
static B GetBAsReturnValue() {return new B();}
//A method that returns an instance of a B as an out parameter
static void GetBAsOutParameter(out B outB) {outB = new B();}


try
static void GetBAsOutParameter(out A outB) {outB = new B();}

[STAThread]
static void Main(string[] args)
{
//The trivial case using a reference of type B
B b;
b = GetBAsReturnValue(); //works fine
GetBAsOutParameter(out b); //works fine

//The case that should be equally trivial but isn't.
//We use a variable of type A instead of B.
//This should be no problem since any reference to an A
//may be assigned values of type B
A a;
a = GetBAsReturnValue(); //works fine as expected.
GetBAsOutParameter(out a); //DOES NOT COMPILE!!!
}


you could not cast (B)a 'cause A isn't delvered from B
}


--
Selvin - Przemek Sulikowski
se****@osadkowski.com.pl


Nov 15 '05 #2

P: n/a
Mårten,

<snip>
The polymorphic rules that applied in the first case don't apply now for
some strange reason. The "polymorphic rules" don't apply because the situation is different.
According "Liskovs Substitutable Principle" you can use B instead of A, but
the opposite is not true. You can't use A instead of B and this is exactly
what happening with GetBAsOutParameter(out a); //DOES NOT COMPILE!!!

Compiler dosen't have any idea how to convert (cast) A to B.

Of course you could provide this information to the compiler, but the bigger
question emerges then in what circumstances should you do so? (Can you
provide an example when such twist is needed?)
Ron
Nov 15 '05 #3

P: n/a
Ron Bullman <ro********@mail.com> wrote:
GetBAsOutParameter(out a); //DOES NOT COMPILE!!!

Compiler dosen't have any idea how to convert (cast) A to B.


In a single-threaded world, it wouldn't need to. The initial value (if
any) in an out parameter can never be used by the called method. The
problem is that in a multi-threaded world, the called method can assign
an initial value and *then* read from the parameter, which may have its
value changed by another thread to a different value which is
compatible with the original variable but not the narrower one in the
called method.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet/
If replying to the group, please do not mail me too
Nov 15 '05 #4

P: n/a
Hi Jon

Thank you both for understanding my problem and for your answer.

I have talked to allot of people about this including Anders Hejlsberg when
I met him at the PDC. Anders gave me an answer but since both of us had been
drinking red wine it was a bit confusing.

Your answer is the first one that I understand.

Thank you,

Mårten

"Jon Skeet" <sk***@pobox.com> wrote in message
news:MP************************@news.microsoft.com ...
Mårten Herberthson <he*********@hotmail.com> wrote:

<snip>
It seems to me like an out parameter actually is behaving more like a ref parameter under the covers.


Yes, an out parameter is *exactly* like a ref parameter under the
covers, with the only difference being that of definite assignment. (An
out parameter doesn't need to be definitely assigned before being used
in the calling method, and must be definitely assigned before the
method returns normally, at which point it is definitely assigned in
the calling method.)

There is, however, one reason why you shouldn't be able to use "out" in
the way you're considering. Bear in mind that the variable shares the
same slot as the original variable - and that another thread may be
able to change the value of that slot while the method is executing.
Consider the following code:

using System;
using System.Threading;

public class Test
{
static object o;

static void Main()
{
new Thread (new ThreadStart (Bar)).Start();
Foo (out o);
}

static void Foo (out string x)
{
x = "hello";
Thread.Sleep (5000);
Console.WriteLine (x.Length);
}

static void Bar ()
{
Thread.Sleep(2000);
o = new object();
}
}

(I haven't put any memory barriers in, but it would be easy to do so.)

The code for Foo assumes that the value of x will always be a reference
to a string or null - but by the time it tries to write x.Length, the
value of o will actually be a reference to a plain object.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet/
If replying to the group, please do not mail me too

Nov 15 '05 #5

P: n/a
Mårten Herberthson <he*********@hotmail.com> wrote:
I have talked to allot of people about this including Anders Hejlsberg when
I met him at the PDC. Anders gave me an answer but since both of us had been
drinking red wine it was a bit confusing.


LOL :)

I'd be interested to see the spec of a language designed *only* during
the periods where the authors were very, very drunk. No doubt it would
be terrible in itself, but I'm sure there'd be some great ideas in
there too - not because no-one could think of them when sober, but
because they might be quickly dismissed as being implausible or even
impossible when sober.

Glad my answer makes sense though.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet/
If replying to the group, please do not mail me too
Nov 15 '05 #6

P: n/a
Jon,

OK, I had wrong understanding how out works. I tought if initialized then it
could be used by the called method, but indeed "The initial value (if any)
in an out parameter can never be used by the called method".
Ron
"Jon Skeet" <sk***@pobox.com> wrote in message
news:MP************************@news.microsoft.com ...
Ron Bullman <ro********@mail.com> wrote:
GetBAsOutParameter(out a); //DOES NOT COMPILE!!!

Compiler dosen't have any idea how to convert (cast) A to B.


In a single-threaded world, it wouldn't need to. The initial value (if
any) in an out parameter can never be used by the called method. The
problem is that in a multi-threaded world, the called method can assign
an initial value and *then* read from the parameter, which may have its
value changed by another thread to a different value which is
compatible with the original variable but not the narrower one in the
called method.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet/
If replying to the group, please do not mail me too

Nov 15 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.