Strange asymmetric polymorphism when using out parameters. Bug?

  • Thread starter Thread starter Ron Bullman
  • Start date Start date
R

Ron Bullman

Mårten,

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
 
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
 
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!!!
}
}
 
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]
- (e-mail address removed)

Mårten Herberthson said:
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 said:
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
 
Ron Bullman said:
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.
 
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
 
Mårten Herberthson said:
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.
 
Back
Top