编程语言
首页 > 编程语言> > .NET等效于Java通配符泛型<?>,具有协方差和反方差?

.NET等效于Java通配符泛型<?>,具有协方差和反方差?

作者:互联网

我试图将一些使用(有界)通配符泛型的Java代码转换为C#.
我的问题是,Java似乎允许泛型类型在与通配符一起使用时既是协变的又是逆变的.例如:

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更难的情况]

解决方法:

您需要将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;
}

有必要重复由IGeneric1< T>指定的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;
}

标签:java,net,covariance,generics,bounded-wildcard
来源: https://codeday.me/bug/20190529/1180046.html