我一直试图将一些使用(有界)通配符泛型的Java代码翻译成C#。 我的问题是,Java似乎允许泛型类型在与通配符一起使用时既是协变的又是逆变的。例如:
爪哇:
interface IInterf { }
class Impl implements IInterf { }
interface IGeneric1<T extends Impl> {
void method1(IGeneric2<?> val);
void method1WithParam(T val);
}
interface IGeneric2<T extends Impl> {
void method2(IGeneric1<?> val);
}
abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> {
public void method1(IGeneric2<?> val2) {
val2.method2(this);
}
}
...作品。
C#等效(?)
interface IInterf { }
class Impl : IInterf { }
interface IGeneric1<T> where T:Impl {
//Java was:
//void method1(IGeneric2<?> val2);
void method1(IGeneric2<Impl> val);
void method1WithParam(T to);
}
interface IGeneric2<T>where T:Impl {
void method2(IGeneric1<Impl> val);
}
abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
//Java was:
//public void method1(IGeneric2<?> val2) {
public void method1(IGeneric2<Impl> val2)
{
val2.method2(this); //'this': Argument type 'Generic<T>' is not
//assignable to parameter type 'IGeneric1<Impl>'
}
public abstract void method1WithParam(T to);
public abstract void method2(IGeneric1<Impl> val);
}
...无法编译 - 请参阅注释中的错误。这是可以预期的,因为IGeneric的通用参数没有为协方差标记为'out'。
如果我改变了这个:
interface IGeneric1<T> where T:Impl {
到这个
interface IGeneric1<out T> where T:Impl
错误消失了,但是出现了另一个错误,因为声明了在同一个接口中采用泛型参数的方法:
interface IGeneric1<T> where T:Impl {
void method1WithParam(T val); //Parameter must be input-safe.
//Invalid variance: The type parameter 'T' must be
//contravariantly valid on 'IGeneric1<out T>'.
建议?
[另请参阅follow-up question更难的情况]
答案 0 :(得分:6)
您需要将Java通配符泛型方法转换为本身通用的C#方法。例如,这个:
interface IGeneric2<T extends Impl> {
void method2(IGeneric1<?> val);
}
应翻译为
interface IGeneric2<T>where T:Impl {
void method2<U>(IGeneric1<U> val) where U:Impl;
}
有必要重复T
指定的IGeneric1<T>
的类型约束作为U
的类型约束。
原因是在Java版本中,method1
和method2
的参数的类型参数存在隐式约束:如果参数必须是某些类似IGeneric1<X>
然后X
显然必须是Impl
,否则它无法为该类型实施IGeneric1
。
在C#中,约束必须是明确的,因此请重复IGeneric1<T>
和IGeneric2<T>
对T
的要求。
所以等效的代码是:
interface IInterf { }
class Impl : IInterf { }
interface IGeneric1<T> where T:Impl {
void method1<U>(IGeneric2<U> val) where U:Impl;
void method1WithParam(T to);
}
interface IGeneric2<T>where T:Impl {
void method2<U>(IGeneric1<U> val) where U:Impl;
}
abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
public void method1<U>(IGeneric2<U> val2) where U:Impl
{
val2.method2(this);
}
public abstract void method1WithParam(T to);
public abstract void method2<U>(IGeneric1<U> val) where U:Impl;
}