Why are there no covariant return types?

S

Stefan Slapeta

Hi,

this code does not compile in C#:

class base_class {}
class derived_class : base_class {}

class A
{
public virtual base_class f()
{
return new base_class();
}
}

class B : A
{
public override derived_class f() // covariant return type
{
return new derived_class();
}
}


Are there any plans to support covariant return types (and maybe
parameter types???) in upcoming C# versions? It could make some OO
designs so much simpler...


Stefan
 
J

Jon Skeet [C# MVP]

Are there any plans to support covariant return types (and maybe
parameter types???) in upcoming C# versions? It could make some OO
designs so much simpler...

I haven't tried it, but I wouldn't be surprised if covariant return
types became available in C#v2, along with generics - I believe in the
Java world, generics made covariance just drop out naturally.

You could always download the .NET v2 beta and try it :)
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hi Stefan,

Frankly I don't see any use of that.

imagin if you have method like this

void Foo(A a)
{
XXX res = a.f();
}

How can you possibly know what XXX should be. The only sure thing is that
XXX == base_class.
In the current version of C# if you use deriv_class as XXX the compiler will
wmit an error in compile type. With covariant return types it cannot do that
and the error will show up at run time.

So what you and up is code like

base_class res = a.f();
if(res is deriv_class)
{
type cast and use deriv class
}
else
{
use base class
}

or some of its variants. Which is almost what we have now, but imagin if
there is deeper hierarcy.

Or you can use something like
deriv_class res;
try
{
res = a.f()
}
catch(invlid type case exception)
{
}

which is...
You can do that with generics because actually you don't need to use
inheritance and virtual methods, but rather you generate a new type.
 
S

Stefan Slapeta

Stoitcho said:
Frankly I don't see any use of that.

This concept (covariance) is supported by a wide range of programming
languages and opens a wide range of possibilities in OO design.
imagin if you have method like this

void Foo(A a)
{
XXX res = a.f();
}

How can you possibly know what XXX should be. The only sure thing is that
XXX == base_class.

Yes, this amount of knowledge is the fundamental idea of what is named
'polymorphism'.
In the current version of C# if you use deriv_class as XXX the compiler will
wmit an error in compile type. With covariant return types it cannot do that
and the error will show up at run time.

No, it will not, of course. It's the same as

BaseClass bc = new Derived();

I just wonder why this quite simple concept is not implemented in C#:
IMO it _must_ be implemented in the CIL (at least in 2.0) because
covariant return types supported by C++.

Stefan
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Could you give us some real life example of where you could use that.
And frankly I don't see how c++ supports that. I would say that c++ may not
check the return type when overriding methods, but to support it I really
doubt it.

Would you say that c# supports covariant return types if it doesn't report
an error? Because honestly I don't see what more it can you do about it.
Yes, you may say that the CLR supports covariant return types because it
simply doesn't check the type of the return value for method overriding and
overloading.
 
S

Stefan Slapeta

Stoitcho said:
Could you give us some real life example of where you could use that.
And frankly I don't see how c++ supports that. I would say that c++ may not
check the return type when overriding methods, but to support it I really
doubt it.

ok, here is a living example in C++ which is not implementable in C#. I
think it explaines itself, if not, just compile and debug it. For
further information where this makes sense: http://tinyurl.com/2s6zp

#include <iostream>

class base {
public:
virtual void g() const {
std::cout << "base" << std::endl;
}
};
class derived : public base {
public:
virtual void g() const {
std::cout << "derived" << std::endl;
}
};

class A {
base m_base;
public:
virtual base const& f() {
return m_base;
}
};

class B : public A {
derived m_derived;
public:
virtual derived const& f() {
return m_derived;
}
};

void some_function(A& a)
{
a.f().g();
}

int main()
{
B b;
some_function(b);
}
 
S

Stoitcho Goutsev \(100\) [C# MVP]

See there is my point if you change in class B the method *f* to return
*base* isnted of derived the result will be the same. So you have no
advantages using covariant return types.

As it goes for the example you post. Those are not managed classes. From the
CLR perspective (if you use ILDasm you can see) those are value types and
are not releated with any inheritance.

If you make them managed classes (*__gc*) and fix all the errors that the
compiler reports you will end up with one error # C2392 that says:

'method' : covariant returns types are not supported in managed types

Anyways I still don't see any advantage using them. Anything, but
porortunity for a bug to creep into my release version.
 
J

Jon Skeet [C# MVP]

Stoitcho Goutsev (100) said:
Could you give us some real life example of where you could use that.

Here's a *really* simple example: ICloneable.

ICloneable defines Clone's return type to be object, when actually you
almost *always* end up casting the result to the type you know it will
be - the same type as the instance you're calling it on.

So, if I want to clone an ArrayList, I need:

ArrayList copy = (ArrayList) original.Clone();

If ArrayList could have declared

ArrayList Clone()
{
....
}

we wouldn't need to do this.

Now in fact, this *is* done all over the framework already, using
explicit interface implementation - look at the various database
classes (SqlConnection, SqlCommand etc). It's a nasty hack around the
lack of covariant return types, and it would have been much nicer if a
single method could have been declared, instead of one for the generic
version and one for the concrete subtype.
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Yeah, besides the fact that theoretically you don't know the type of the
object, which Clone methods you call.

And again in theory one should go with the most generic base type and then
do the conversion.
 
M

Mike Schilling

Jon Skeet said:
I haven't tried it, but I wouldn't be surprised if covariant return
types became available in C#v2, along with generics - I believe in the
Java world, generics made covariance just drop out naturally.

Java actually uses a different mechanism for covariants, one that's so
simple that the generated bytecode works in JVMs back to (at least) 1.3,
though the 1.5 compiler is the first to allow the syntax.

class Base { Object foo() { ...} }
class Derived extends Base { String foo() {...} }

is compiled as if Derived also defined a method

Object foo() { return String-version-of-foo(); }

This makes the dispatch of Base.foo() do the right thing.
 
S

Stefan Slapeta

Stoitcho said:
Yeah, besides the fact that theoretically you don't know the type of the
object, which Clone methods you call.

And again in theory one should go with the most generic base type and then
do the conversion.

In theory, you should avoid any cast if possible because every cast is a
potential runtime error!

And here - *with* covariant return types - you *for sure* know what
class you are dealing with *and* you can avoid any cast.

ArrayList original;
ArrayList copy = original.Clone();


Stefan
 
S

Stefan Slapeta

Stoitcho said:
'method' : covariant returns types are not supported in managed types

And so I would be interested whether they are supported in C# 2.0; I
think they _are_ in the C++.net release.
Anyways I still don't see any advantage using them. Anything, but
porortunity for a bug to creep into my release version.

I think there you have not understood this concept so far: using
covariant return types _can not_ lead to errors you don't also get
without them!

Stefan
 
S

Stefan Slapeta

Stoitcho said:
See there is my point if you change in class B the method *f* to return
*base* isnted of derived the result will be the same. So you have no
advantages using covariant return types.

I know what you mean - maybe the example was not the best. Covariant
return types of coure only make sense if you deal *not only* with the
base class (or any interface), but also with the (return types of the)
derived class. Jon's example maybe provides the best possible explanation.

Stefan
 
S

Scott Allen

And so I would be interested whether they are supported in C# 2.0; I
think they _are_ in the C++.net release.

Hi Stefan:

Putting the code from your original post into Beta 1 of the 2005
compiler produces:

'Testt.B.f()': return type must be 'Test.base_class' to match
overridden member 'Test.A.f()'
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hmm, there are always cases where something can be useful. I said in theory
becuse I can't see any possible way to know what is the type in this
situation

void Foo(ArrayList list)
{
XXX copy = list.Clone();

//What is XXX? The simple answer is ArrayList. But is it?

}

A dont see many cases where you create a list and cloned. Normaly you
received as a parameter or it is property of some object. In this case you
cannot know.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top