5

(制限付きの)ワイルドカードジェネリックを使用する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>'.

提案?

[やや難しいシナリオについては、フォローアップの質問も参照してください]

4

1 に答える 1

7

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バージョンでは、およびのパラメーターの型引数に暗黙の制約があるためです。パラメーターが何らかの種類である必要がある場合は、明らかにその型に実装できないためです。method1method2IGeneric1<X>XImplIGeneric1

IGeneric1<T>C#では、制約は明示的である必要があるため、。のwhatとIGeneric2<T>requireを繰り返します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;
}
于 2013-01-11T11:57:38.450 に答える