.NET等效于Java有界通配符(IInterf <?>)?

Cri*_*scu 6 .net java generics covariance bounded-wildcard

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

[这是关于一个更简单的有界通配符案例的前一个问题的衍生物]

Java - 作品:

class Impl { }

interface IGeneric1<T extends Impl> {
    void method1(IGeneric2<?> val);
    T method1WithParam(T val);
}

interface IGeneric2<T extends Impl> {
    void method2(IGeneric1<?> val);
}

abstract class Generic2<T extends Impl> implements IGeneric2<T> {

    // !! field using wildcard 
    protected IGeneric1<?> elem;

    public void method2(IGeneric1<?> val1) {
        val1.method1(this);

        //assignment from wildcard to wildcard
        elem = val1;
    }
}

abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> {

    public void method1(IGeneric2<?> val2) {
        val2.method2(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

C# - 不编译......

class Impl { }

interface IGeneric1<T> where T:Impl {
  //in Java:
  //void method1(IGeneric2<?> val);
    void method1<U>(IGeneric2<U> val) where U : Impl; //see this Q for 'why'
                                 // https://stackoverflow.com/a/14277742/11545

    T method1WithParam(T to);
}

interface IGeneric2<T>where T:Impl {
    void method2<U>(IGeneric1<U> val) where U : Impl;
}

abstract class Generic2<T, TU>: IGeneric2<T> //added new type TU
    where T : Impl
    where TU : Impl
{
  //in Java:
  //protected IGeneric1<?> elem;
    protected IGeneric1<TU> elem;

  //in Java:
  //public void method2(IGeneric1<?> val1) 
    public void method2<U>(IGeneric1<U> val) 
        where U : TU //using TU as constraint
    {
        elem = val;  //Cannot convert source type 'IGeneric1<U>' 
                     //to target type 'IGeneric1<TU>'
    }
    public abstract void method1WithParam(T to);
}

abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
  //in Java:
  //public void method1(IGeneric2<?> val2) 
    public void method1<U>(IGeneric2<U> val2) where U : Impl
    {
         val2.method2(this);
    }

    public abstract T method1WithParam(T to);
    public abstract void method2<U>(IGeneric1<U> val) where U : Impl;
    public abstract void nonGenericMethod();
}
Run Code Online (Sandbox Code Playgroud)

如果我改变interface IGeneric1<T>interface IGeneric1<out T> 上述错误消失,但method1WithParam(T)抱怨方差:

Parameter must be input-safe. Invalid variance: The type parameter 'T' must be
contravariantly valid on 'IGeneric1<out T>'.
Run Code Online (Sandbox Code Playgroud)

Jon*_*Jon 3

首先我要说的是,设计审查显然已经开始按顺序进行了。原始的 Java 类聚合了一个IGeneric1<?>成员,但如果不知道其类型参数,就不可能method1WithParam以类型安全的方式调用它。

这意味着elem只能用于调用其method1成员,其签名不依赖于 的类型参数IGeneric1。由此可见,method1可以分解为非泛型接口:

// C# code:
interface INotGeneric1 {
    void method1<T>(IGeneric2<T> val) where T : Impl;
}

interface IGeneric1<T> : INotGeneric1 where T : Impl {
    T method1WithParam(T to);
}
Run Code Online (Sandbox Code Playgroud)

之后,class Generic2可以聚合一个INotGeneric1成员:

abstract class Generic2<T>: IGeneric2<T> where T : Impl
{
    protected INotGeneric1 elem;

    // It's highly likely that you would want to change the type of val
    // to INotGeneric1 as well, there's no obvious reason to require an
    // IGeneric1<U>
    public void method2<U>(IGeneric1<U> val) where U : Impl
    {
        elem = val; // this is now OK
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,现在elem.method1WithParam除非您诉诸强制转换或反射,否则您无法调用,即使已知存在这样的方法并且它是通用的,并且以某种未知类型X作为类型参数。然而,这与 Java 代码具有相同的限制;只是 C# 编译器不会接受此代码,而 Java 只会在您尝试调用method1WithParam1.