使用通用接口约束时的协变/逆变难题

发布于 2024-11-09 09:22:32 字数 979 浏览 0 评论 0原文

    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,大概是因为 U 是设置为 out 意味着它可以接受较少的派生。但是,DerivedBase 更派生,因此会生成编译器错误。

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

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

    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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

够运 2024-11-16 09:22:32

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

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

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

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

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

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

更新:在您发表评论后,我发现最大的困境是您希望它既可以输入也可以输出,这实际上是不可能的,因为它是逆变输入,所以您不能将接口协变分配给 IFooIFoo<不幸的是,IShape,Base>

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

public class MyFoo : IFoo;

然后在实现中转换为Rectangle。最主要的是,同一类型参数不能同时具有协变和逆变。

这有道理吗?

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?

执妄 2024-11-16 09:22:32

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文