Why an inherited interface can't be converted to its base interface in generic context?

前端 未结 2 1135
广开言路
广开言路 2020-12-04 03:22

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:



        
相关标签:
2条回答
  • 2020-12-04 03:55

    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();.

    0 讨论(0)
  • 2020-12-04 04:06

    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();
    }
    
    0 讨论(0)
提交回复
热议问题