.NET等效于Java通配符泛型<! - ? - >具有共同和反差?

时间:2013-01-11 11:40:25

标签: java .net generics covariance bounded-wildcard

我一直试图将一些使用(有界)通配符泛型的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更难的情况]

1 个答案:

答案 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版本中,method1method2的参数的类型参数存在隐式约束:如果参数必须是某些类似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;
}