使用通用接口约束时的协变/逆变难题
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 意味着它可以接受较少的派生。但是,Derived
比 Base
更派生,因此会生成编译器错误。
这个例子是人为的,但我们正在处理的实现是从工厂返回 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的 IFoo 接口在这种用法中似乎是错误的,它应该是:
随着
U
退出。请记住,out
泛型类型参数意味着它可以“向外”变化。也就是说,您可以隐式地将类型扩展为更宽的类型。不过,In
意味着您可以隐式地将类型“向内”缩小为更具体的类型。当然,这些只是粗略的类比。因此,在分配
hmm
的情况下,您隐式地尝试将U
的接口泛型类型参数从Derived
扩大到Base
,但接口声明它是缩小的(in
):因此它无法进行隐式转换。如果您确实希望能够隐式扩展此接口,则第二个类型参数应该是
out
而不是in
。更新:在您发表评论后,我发现最大的困境是您希望它既可以输入也可以输出,这实际上是不可能的,因为它是逆变输入,所以您不能将接口协变分配给 IFoo
IFoo<不幸的是,IShape,Base>
。您要么需要围绕无法分配给
IFoo
的事实进行编码,要么您可以将 Foo 创建为:public class MyFoo : IFoo;
然后在实现中转换为
Rectangle
。最主要的是,同一类型参数不能同时具有协变和逆变。这有道理吗?
Your IFoo interface seems to be wrong in this usage, it should be:
With
U
being out. Remember that anout
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 forU
fromDerived
toBase
, but the interface declares it to be narrowing (in
):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 ofin
.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?
可以将 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.