I\'m trying to implement an interface inheritance system in my C# project, but I can\'t get it to work.
Here is a simplified version:
Let's have a play with your types and call them something different.
public interface IFruit { }
public abstract class BowlOf<Fruit> where Fruit : IFruit
{
public void Add(Fruit fruit) { }
}
public class Apple : IFruit { }
public class BowlOfApples : BowlOf<Apple> { }
Now, with that - which is pretty much just a rename of the types (but changing public interface ChildInterface : BaseInterface {}
to public class Apple : IFruit { }
) then we create the following issue.
Let's say I have public class Banana : IFruit { }
also and let's assume that the following is legal:
BowlOf<IFruit> c = new BowlOfApples();
Then I am perfectly fine to call c.Add(new Banana())
. What!?! You can't add a banana to a bowl of apples.
And that's why the compiler is complaining when you try to do AbstractClass<BaseInterface> c = new ConcreteClass();
.
You aren't able to make the assignment because the base class, AbstractClass<T>
, is invariant. What you want to be able to make that kind of assignment is a covariant type. Defining Covariance and Contravariance is limited to interfaces, so that means we need another interface.
public interface IAbstractClass<out T> where T : BaseInterface { }
public abstract class AbstractClass<T> : IAbstractClass<T> where T : BaseInterface { }
The out
keyword marks the generic type parameter as covariant. We then implement that interface in AbstractClass<T>
, and our other types can work expected through the interface. These are also the only alterations we need to make, we leave the other type definitions the same:
public interface BaseInterface { }
public interface ChildInterface : BaseInterface { }
public class ConcreteClass : AbstractClass<ChildInterface> { }
We now have a covariant interface that AbstractClass<T>
implements, and you can do the kind of assignment you desire, but you'll have to target the IAbstractClass
interface.
public void Main() {
IAbstractClass<BaseInterface> c = new ConcreteClass();
}