Covariance/Contravariance Conundrum when using generic interface constraints

不想你离开。 提交于 2020-01-03 19:35:25

问题


    public interface IShape{}

    public class Rectangle : IShape{}

    public class Base{}

    public class Derived : Base{}

    public interface IFoo<out T, in U>
        where T : IShape
        where U : Base
    {
        T Convert(U myType);
    }

    public class MyFoo : IFoo<Rectangle, Derived>
    {
        public Rectangle Convert(Derived myType)
        {
            throw new NotImplementedException();
        }
    }    

    class Program
    {
        static void Main(string[] args)
        {
            IFoo<IShape, Base> hmm = new MyFoo();
        }
    }

Given the above code, the compiler is unable to determine how to assign the type MyFoo to IFoo<IShape, Base>, presumably because U is set as an out meaning that it can accept less derived. However, Derived is, well, more derived than Base, so generates a compiler error.

This example is contrived but the implementation we are dealing with is one in which MyFoo would be returned from a factory.

Although U is used as a parameter, it is also an output when trying to assign it to the generic interface but I am unable to use the out keyword here. How could we work around this?


回答1:


Your IFoo interface seems to be wrong in this usage, it should be:

public interface IFoo<out T, **out** U>

With U being out. Remember that an out generic type parameter means that it can vary "outwards". That is, you can widen the type implicitly to a wider type. In, though, means that you can implicitly narrow the type "inwards" to a more specific type. These are just rough analogies, of course.

So in the case of the assignment of hmm, you are are implicitly trying to widen the interface generic type parameter for U from Derived to Base, but the interface declares it to be narrowing (in):

IFoo<IShape, Base> hmm = new MyFoo();

So it can't make the implicit conversion. If you really want to be able to widen this interface implicitly, the second type argument should be out instead of in.

Update: after your comments, I see that the big dilemma is that you want it to be both in and out, which isn't really possible Since it's a contravariant input, you can't assign the interface covariantly to IFoo<IShape, Base>, unfortunately.

You either need to code around the fact you can't assign to IFoo<IShape,Base> or what you could do is create Foo as:

public class MyFoo : IFoo<Rectangle, Base>

And then cast to Rectangle inside the implementation. The main thing is you can't have both covariance and contravariance on the same type parameter.

Does this make sense?




回答2:


Something that can convert a Base into a Rectangle will also turn a Derived into an IShape. Something that can convert a Derived into a Rectangle, however, may not be able to do anything useful with a Base. You correctly identified that the covariance specifier for your second parameter needs to be "in", but are then trying to use the covariance in the way opposite what it actually supports.



来源:https://stackoverflow.com/questions/6126741/covariance-contravariance-conundrum-when-using-generic-interface-constraints

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!