Why doesn't inheritance work the way I think i

2019-06-16 21:24发布

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.

17条回答
疯言疯语
2楼-- · 2019-06-16 21:59
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.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-06-16 22:00

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.

查看更多
Lonely孤独者°
4楼-- · 2019-06-16 22:01

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();}
 } 
查看更多
叼着烟拽天下
5楼-- · 2019-06-16 22:02

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

查看更多
孤傲高冷的网名
6楼-- · 2019-06-16 22:02

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>?

查看更多
做自己的国王
7楼-- · 2019-06-16 22:03

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.

查看更多
登录 后发表回答