使用通用接口约束时的协方差/反向差异难题

时间:2011-05-25 15:14:10

标签: c# generics interface covariance contravariance

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

鉴于上面的代码,编译器无法确定如何将类型MyFoo分配给IFoo<IShape, Base>,大概是因为U被设置为out意味着它可以接受更少的派生。但是,DerivedBase派生的更多,因此会生成编译器错误。

这个例子是设计的,但我们正在处理的实现是从工厂返回MyFoo的实现。

虽然U用作参数,但在尝试将其分配给通用界面时它也是输出,但我在此处无法使用out关键字。我们怎么能解决这个问题?

2 个答案:

答案 0 :(得分:1)

您的IFoo界面在此用法中似乎有误,应该是:

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

U出局。请记住,out泛型类型参数意味着它可以“向外”变化。也就是说,您可以隐式地将类型扩展为更宽的类型。但是,In意味着您可以隐式地将“内部”类型缩小为更具体的类型。当然,这些只是粗略的比喻。

因此,在分配hmm的情况下,您隐含地尝试将U的接口泛型类型参数从Derived扩展到Base,但是interface声明它变窄(in):

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

所以它无法进行隐式转换。如果您真的希望能够隐式扩展此接口,则第二个类型参数应为out而不是in

更新:在您发表评论之后,我发现最大的难题是您希望它既可以进出又出现,这实际上是不可能的因为它是一个逆变输入,所以您不能将该接口共同分配给{{不幸的是,1}}。

您需要围绕无法分配给IFoo<IShape, Base>的事实进行编码,或者您可以做的是创建Foo:

IFoo<IShape,Base>

然后在实现中转换为public class MyFoo : IFoo<Rectangle, Base>。主要的是你不能在同一类型参数上同时具有协方差和逆变。

这有意义吗?

答案 1 :(得分:0)

可以将Base转换为Rectangle的东西也会将Derived转换为IShape。但是,可以将Derived转换为Rectangle的东西可能无法对Base执行任何有用的操作。您正确地确定了第二个参数的协方差说明符需要“在”中,然后尝试以与实际支持相反的方式使用协方差。