I have a set of classes from a 3rd party library. These classes use an inheritance structure to share logic. I would like to add a layer of abstraction in the middle of their inheritance tree to add functionality to all of the children (concrete) implementations.
Here is a simplified example of the classes in the 3rd party lib:
public interface IAnimal
{
bool IsMammal { get; }
}
public abstract class Animal : IAnimal
{
public abstract bool IsMammal { get; }
public string Name { get; set; }
}
public class Cat : Animal
{
public override bool IsMammal { get { return true; } }
public void Pur() {}
}
public class Dog : Animal
{
public override bool IsMammal { get { return true; } }
public void Fetch() {}
}
public class Snake : Animal
{
public override bool IsMammal { get { return false; } }
public void ShedSkin() {}
}
I would like to add the concept of an AnimalWithSuperPower
. These types of animals should have 1 additional Property; SuperPower
. I would like to be able to have classes like CatWithSuperPower
which derive from Cat
, AnimalWithSuperPower
, & Animal
so that I can access all the functionality of those.
Here is the definition of SuperPower
:
public enum SuperPower { Invisibility, SuperStrength, XRayVision }
My first idea was to use multiple inheritance. But unfortunately, C# doesn't support multiple base classes.
private abstract class AnimalWithSuperPower : Animal
{
public SuperPower SuperPower { get; set; }
}
// doesn't compile because you can't extend 2 classes
private class DogWithSuperPower : AnimalWithSuperPower, Dog {}
My next attempt uses a combination of inheritance, composition, and generics to try to deliver the functionality of the base classes.
private abstract class AnimalWithSuperPower<TAnimalType> : Animal where TAnimalType : IAnimal
{
public SuperPower SuperPower { get; set; }
protected readonly TAnimalType Animal;
protected AnimalWithSuperPower()
{
Animal = (TAnimalType) Activator.CreateInstance(typeof(TAnimalType));
}
}
private class SuperCat : AnimalWithSuperPower<Cat>
{
public override bool IsMammal { get { return Animal.IsMammal; } }
}
private class SuperCatWithPur : AnimalWithSuperPower<Cat>
{
public override bool IsMammal { get { return Animal.IsMammal; } }
public void Pur() // needing duplicate pass-through methods/properties like this is painful :(
{
Animal.Pur();
}
}
private static void ExampleUsage()
{
var invisibleCat = new SuperCat { SuperPower = SuperPower.Invisibility };
invisibleCat.Pur(); // doesn't compile - can't access Pur() method because doesn't extend Cat
var xrayCat = new SuperCatWithPur { SuperPower = SuperPower.XRayVision };
xrayCat.Pur(); // only works because I exposed the method with the EXACT same signature
}
This solution is not very good (IMO) because of these reasons:
SuperCat
andSuperCatWithPur
aren't actually instances ofCat
- any method you wish to use from
Cat
needs to be mirrored in the container class - feels kind of messy:
AnimalWithSuperPower
is anAnimal
but it also takes anAnimal
type parameter
I also tried doing it with an extension method but it wasn't any better than the above two attempts:
private abstract class AnimalWithSuperPower : Animal
{
public SuperPower SuperPower { get; set; }
}
private static AnimalWithSuperPower WithSuperPower(this Animal animal, SuperPower superPower)
{
var superAnimal = (AnimalWithSuperPower) animal;
superAnimal.SuperPower = superPower;
return superAnimal;
}
private static void ExampleUsage()
{
var dog = new Dog { Name = "Max" };
var superDog = dog.WithSuperPower(SuperPower.SuperStrength);
superDog.Fetch(); // doesn't compile - superDog isn't an instance of Dog
}
If I had control of the 3rd party classes, I could likely do this cleanly by introducing a new class in the middle of the inheritance tree, but I can't
My Question:
How can I model AnimalWithSuperPower
so that:
- instances are considered of types
Cat
(or appropriate sub-class),Animal
, &AnimalWithSuperPower
- all the methods and properties are available without extra pass-through calls