Implementing nested generic Interfaces

前端 未结 3 1316
面向向阳花
面向向阳花 2020-12-14 21:01

I have the following Classes / Interfaces:

// Model
public class A : IA { }
// ModelLogic
public class B : IB { }

// Model Interface
public interfa         


        
相关标签:
3条回答
  • It's not easy to see when you have empty interfaces. Consider you have one method M in interface IB:

    public interface IB<T> where T : IA 
    { 
        void M(T t); 
    }
    

    And here is implementation of B:

    public class B : IB<A>
    {
        public void M(A t)
        {
            // only object of type A accepted 
        }
    }
    

    Then you have object C, which also implements IA:

    public class C : IA { } 
    

    So, if your code would be possible, then you could call:

    IB<IA> foo = new B();
    foo.M(new C());
    

    Problem is that class B accepts only objects of type A. Error!

    0 讨论(0)
  • 2020-12-14 21:38

    OK, let's replace A with Fish, IA with IAnimal, B with Aquarium, and IB<T> with IContainer<T>. And we'll add a member to IContainer<T>, and a second implementation of IAnimal:

    // Model
    public class Fish : IAnimal { }
    public class Tiger : IAnimal { }
    // ModelLogic
    public class Aquarium : IContainer<Fish> 
    { 
        public Fish Contents { get; set; }
    }
    
    // Model Interface
    public interface IAnimal { }
    // ModelLogic Interface
    public interface IContainer<T> where T : IAnimal 
    { 
        T Contents { get; set; }
    }
    
    IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
    foo.Contents = new Tiger(); // Because this is legal!
    

    You can put a Tiger into foo -- foo is typed as a container that can contain any animal. But you can only put a Fish into an Aquarium. Since the operations you can legally perform on an Aquarium are different than the operations you can perform on an IContainer<IAnimal>, the types are not compatible.

    The feature you want is called generic interface covariance and it is supported by C# 4, but you have to prove to the compiler that you will never put a tiger into your fish tank. What you want to do is:

    // Model
    public class A : IA { }
    // ModelLogic
    public class B : IB<A> { }
    
    // Model Interface
    public interface IA { }
    // ModelLogic Interface
    public interface IB<out T> where T : IA { }
    

    Notice the covariance annotation on IB. This out means that T can only be used as an output, not as an input. If T is only an output then there is no way for someone to put a tiger into that fish tank because there is no "put into" property or method possible.

    I wrote a number of blog articles while we were adding that feature to C#; if you are interested in the design considerations that went into the feature, see:

    http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

    0 讨论(0)
  • 2020-12-14 21:40

    To fix your code, just change

    public interface IB<T> where T : IA { }
    

    to

    public interface IB<out T> where T : IA { }
    
    0 讨论(0)
提交回复
热议问题