使用通用接口约束时的协方差/反方差Conundrum

本文关键字:方差 Conundrum 接口 约束 | 更新日期: 2023-09-27 17:57:35

    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关键字。我们如何解决这个问题?

使用通用接口约束时的协方差/反方差Conundrum

您的IFoo接口在这种用法中似乎是错误的,它应该是:

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

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

因此,在分配hmm的情况下,您隐含地试图将U的接口泛型类型参数从Derived扩展到Base,但接口声明它正在缩小(in(:

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

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

更新:在你的评论之后,我发现最大的困境是你希望它既输入又输出,这是不可能的。不幸的是,由于它是一个逆变输入,你不能将接口协变地分配给IFoo<IShape, Base>

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

public class MyFoo : IFoo<Rectangle, Base>

然后在实现内部强制转换为Rectangle。最重要的是,同一类型的参数不能同时具有协方差和反方差。

这有道理吗?

可以将Base转换为Rectangle的东西也会将Derived转换为IShape。然而,一些可以将"派生"转换为"矩形"的东西可能无法对"基"执行任何有用的操作。您正确地确定了第二个参数的协方差说明符需要为"in",但随后尝试以与实际支持相反的方式使用协方差。