views:

771

answers:

17

I'm having some inheritance issues as I've got a group of inter-related abstract classes that need to all be overridden together to create a client implementation. Ideally I would like to do something like the following:

abstract class Animal
{
  public Leg GetLeg() {...}
}

abstract class Leg { }

class Dog : Animal
{
  public override DogLeg Leg() {...}
}

class DogLeg : Leg { }

This would allow anyone using the Dog class to automatically get DogLegs and anyone using the Animal class to get Legs. The problem is that the overridden function has to have the same type as the base class so this will not compile. I don't see why it shouldn't though, since DogLeg is implicitly castable to Leg. I know there are plenty of ways around this, but I'm more curious why this isn't possible/implemented in C#.

EDIT: I modified this somewhat, since I'm actually using properties instead of functions in my code.

EDIT: I changed it back to functions, because the answer only applies to that situation (covariance on the value parameter of a property's set function shouldn't work). Sorry for the fluctuations! I realize it makes a lot of the answers seem irrelevant.

+2  A: 

GetLeg() must return Leg to be an override. Your Dog class however, can still return DogLeg objects since they are a child class of Leg. clients can then cast and operate on them as doglegs.

public class ClientObj{
    public void doStuff(){
    Animal a=getAnimal();
    if(a is Dog){
       DogLeg dl = (DogLeg)a.GetLeg();
    }
  }
}
shsteimer
+6  A: 

Dog should return a Leg not a DogLeg as the return type. The actual class may be a DogLeg, but the point is to decouple so the user of Dog doesn't have to know about DogLegs, they only need to know about Legs.

Change:

class Dog : Animal
{
  public override DogLeg GetLeg() {...}
}

to:

class Dog : Animal
{
  public override Leg GetLeg() {...}
}

Don't Do this:

 if(a instanceof Dog){
       DogLeg dl = (DogLeg)a.GetLeg();

it defeats the purpose of programing to the abstract type.

The reason to hide DogLeg is because the GetLeg function in the abstract class returns an Abstract Leg. If you are overriding the GetLeg you must return a Leg. Thats the point of having a method in an abstract class. To propagate that method to it's childern. If you want the users of the Dog to know about DogLegs make a method called GetDogLeg and return a DogLeg.

If you COULD do as the question asker wants to, then every user of Animal would need to know about ALL animals.

Brian Leahy
Irrelevant. The question asks why can't a subtype override a method with a method whose return be a subtype of the overridee's return.
Ivan Hamilton
I can understand the argument that the user of Animal should only know about Leg objects, but I'm less convinced that this should be hidden from the users of the Dog class.
Luke
A: 

Right, I understand that I can just cast, but that means the client has to know that Dogs have DogLegs. What I'm wondering is if there are technical reasons why this isn't possible, given that an implicit conversion exists.

Luke
+14  A: 

The short answer is that GetLeg is invariant in its return type. The long answer can be found here: Covariance and contravariance

I'd like to add that while inheritance is usually the first abstraction tool that most developers pull out of their toolbox, it is almost always possible to use composition instead. Composition is slightly more work for the API developer, but makes the API more useful for its consumers.

Apocalisp
Ivan Hamilton
Those articles you linked to are about generic type variance. The question is about return type covariance. I explicitly state in those articles that I am *not* talking about return type covariance.
Eric Lippert
Eric Lippert: But those things are intrinsically connected. See here: http://apocalisp.wordpress.com/2009/08/27/hostility-toward-subtyping/
Apocalisp
A: 

@Brian Leahy Obviously if you are only operating on it as a Leg there is no need or reason to cast. But if there is some DogLeg or Dog specific behavior, there are sometimes reasons that the cast is neccessary.

shsteimer
+2  A: 

The concept that is causing you problems is described at http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

ben
A: 

@Luke

I think your perhaps misunderstanding inheritance. Dog.GetLeg() will return a DogLeg object.

public class Dog{
    public Leg GetLeg(){
         DogLeg dl = new DogLeg(super.GetLeg());
         //set dogleg specific properties
    }
}


    Animal a = getDog();
    Leg l = a.GetLeg();
    l.kick();

the actual method called will be Dog.GetLeg(); and DogLeg.Kick() (I'm assuming a method Leg.kick() exists) there for, the declared return type being DogLeg is unneccessary, because that is what returned, even if the return type for Dog.GetLeg() is Leg.

shsteimer
A: 

You could also return the interface ILeg that both Leg and/or DogLeg implement.

Keith Sirmons
+3  A: 
abstract class Animal
{
  public virtual Leg GetLeg ()
}

abstract class Leg { }

class Dog : Animal
{
  public override Leg GetLeg () { return new DogLeg(); }
}

class DogLeg : Leg { void Hump(); }

Do it like this, then you can leverage the abstraction in your client:

Leg myleg = myDog.GetLeg();

Then if you need to, you can cast it:

if (myleg is DogLeg) { ((DogLeg)myLeg).Hump()); }

Totally contrived, but the point is so you can do this:

foreach (Animal a in animals)
{
   a.GetLeg().SomeMethodThatIsOnAllLegs();
}

While still retaining the ability to have a special Hump method on Doglegs.

FlySwat
+1  A: 

Perhaps it's easier to see the problem with an example:

Animal dog = new Dog();
dog.SetLeg(new CatLeg());

Now that should compile if you're Dog compiled, but we probably don't want such a mutant.

A related issue is should Dog[] be an Animal[], or IList<Dog> an IList<Animal>?

Tom Hawtin - tackline
A: 

The important thing to remember is that you can use a derived type every place you use the base type (you can pass Dog to any method/property/field/variable that expects Animal)

Let's take this function:

public void AddLeg(Animal a)
{
   a.Leg = new Leg();
}

A perfectly valid function, now let's call the function like that:

AddLeg(new Dog());

If the property Dog.Leg isn't of type Leg the AddLeg function suddenly contains an error and cannot be compiled.

Nir
+1  A: 

Not that it is much use, but it is maybe interesting to note the Java does support covariant returns, and so this would work exactly how you hoped. Except obviously that Java doesn't have properties ;)

serg10
+8  A: 

Clearly, you'll need a cast if you're operating on a broken DogLeg.

vextasy
hahaha nice play on words :)
Brian Leahy
+3  A: 

You can use generics and interfaces to implement that in C#:

abstract class Leg { }

interface IAnimal { Leg GetLeg(); }

abstract class Animal<TLeg> : IAnimal where TLeg : Leg
 { public abstract TLeg GetLeg();
   Leg IAnimal.GetLeg() { return this.GetLeg(); }
 }

class Dog : Animal<Dog.DogLeg>
 { public class DogLeg : Leg { }
   public override DogLeg GetLeg() { return new DogLeg();}
 }
Mark Cidade
Be aware that this elegant looking solution may lead to complexities due to C# lack of generic variance support.
Ivan Hamilton
what kind of complexities? This solution doesn't require any variance.
Mark Cidade
what if we need a more complex Dog? One that have a DogTail, DogTeeth, DogEtc..
Seiti
You can either add a type parameter for each body part (e.g., Animal<TLeg, TTail, TTeeth>) or put all body parts in an object and delegate the peoperties to the object: "Animal<TParts> where TParts : PartsBase, new()" and "Dog : Animal<DogParts>"
Mark Cidade
+4  A: 

It is a perfectly valid desire to have the signature an overriding method have a return type that is a subtype of the return type in the overridden method (phew). After all, they are run-time type compatible.

But C# does not yet support "covariant return types" in overridden methods (unlike C++ [1998] & Java [2004]).

You'll need to work around and make do for the foreseeable future, as Eric Lippert stated in his blog [June 19, 2008]:

That kind of variance is called "return type covariance".

we have no plans to implement that kind of variance in C#.

Ivan Hamilton
A: 

You can achieve what you want by using a generic with an appropriate constraint, like the following:

abstract class Animal<LegType> where LegType : Leg
{
    public abstract LegType GetLeg();
}

abstract class Leg { }

class Dog : Animal<DogLeg>
{
    public override DogLeg GetLeg()
    {
        return new DogLeg();
    }
}

class DogLeg : Leg { }
+1  A: 

C# has explicit interface implementations to address just this issue:

abstract class Leg { }
class DogLeg : Leg { }

interface IAnimal
{
    Leg GetLeg();
}

class Dog : IAnimal
{
    public override DogLeg GetLeg() { /* */ }

    Leg IAnimal.GetLeg() { return GetLeg(); }
}

If you have a Dog through a reference of type Dog, then calling GetLeg() will return a DogLeg. If you have the same object, but the reference is of type IAnimal, then it will return a Leg.

munificent