21

次のように、同じクラスに対してインターフェイスを 2 回実装しようとすると、コンパイラ エラーが発生しました。

public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1>
{
   /* implementation for IMapper<T1, T2> here.  */

   /* implementation for IMapper<T2, T1> here.  */
}

エラー:

'Mapper' は 'IMapper' と 'IMapper' の両方を実装することはできません。これらは一部の型パラメーターの置換で統合される可能性があるためです。

この回避策が機能するのはなぜですか? 私は問題を解決したのか、それとも単にコンパイラを騙したのか疑問に思っています.

public class Mapper<T1, T2> : MapperBase<T1, T2>, IMapper<T1, T2>
{
    /* implementation for IMapper<T1, T2> here. */
}

public class MapperBase<T1, T2> : IMapper<T2, T1>
{
    /* implementation for IMapper<T2, T1> here. */
}

編集MyClass: 、MyClassBase、およびIMyInterfaceMapperMapperBase、および に更新してIMapper、この問題が発生する可能性があるより現実的なシナリオを表します。

4

3 に答える 3

22

この実装を検討してください。

public class MyClass<T1, T2> : IMyInterface<T1, T2>, IMyInterface<T2, T1>
{
   /* implementation for IMyInterface<T1, T2> here.  */

   /* implementation for IMyInterface<T2, T1> here.  */
}

何がMyClass<int, int>実装されていますか?とが等しい場合に と を統合するため、IMyInterface<int, int>2 回実装します。そのため、 と の両方を同じクラスに実装することは許可されていません。たとえばandを実装しようとすると、同じ理由が適用されます。型式は for を統一します。IMyInterface<T1, T2>IMyInterface<T2, T1>T1T2IMyInterface<T1, T2>IMyInterface<T2, T1>IMyInterface<int, T1>IMyInterface<T2, double>T1 = double, T2 = int

この実装を検討してください。

public class MyClass<T1, T2> : MyClassBase<T1, T2>, IMyInterface<T1, T2>
{
    /* implementation for IMyInterface<T1, T2> here. */
}

public class MyClassBase<T1, T2> : IMyInterface<T2, T1>
{
    /* implementation for IMyInterface<T2, T1> here. */
}

あなたがしたことは、 を優先するIMyInterface<T1, T2>ことIMyInterface<T2, T1>です。T1T2が等しく、 のインスタンスがある場合、MyClass<T1, T2>実装IMyInterface<T1, T2>が選択されます。のインスタンスがある場合はMyBaseClass<T1, T2>IMyInterface<T2, T1>実装が選択されます。

これは、動作を示すおもちゃのプログラムです。特にa_as_i.M(0, 1)との動作に注意してa_as_b.M(0, 1)ください。(メソッド名の前に を付けて) をI<T2, T1>明示的に実装する場合、コンパイル時の構文を使用して呼び出すことはできません。反省は必要でしょう。B<T1, T2>I<T2, T1>.

interface I<T1, T2>
{
    void M(T1 x, T2 y);
}

class A<T1, T2> : B<T1, T2>, I<T1, T2>
{
    public void M(T1 x, T2 y)
    {
        Console.WriteLine("A: M({0}, {1})", x, y);
    }
}

class B<T1, T2> : I<T2, T1>
{
    public void M(T2 x, T1 y)
    {
        Console.WriteLine("B: M({0}, {1})", x, y);
    }
}

class Program
{
    static void Main(string[] args)
    {
        //Outputs "A: M(0, 1)"
        var a = new A<int, int>();
        a.M(0, 1);

        //Outputs "B: M(0, 1)"
        var b = new B<int, int>();
        b.M(0, 1);

        //Outputs "A: M(0, 1)" because I<T1, T2>
        //takes precedence over I<T2, T1>
        var a_as_i = a as I<int, int>;
        a_as_i.M(0, 1);

        //Outputs "B: M(0, 1)" despite being called on an instance of A
        var a_as_b = a as B<int, int>;
        a_as_b.M(0, 1);

        Console.ReadLine();
    }
}
于 2014-03-28T19:54:49.333 に答える
1

あなたはコンパイラを騙しているのではなく、競合する関数定義を持たないようにしています。インターフェイスに関数があると仮定しますstring Convert(T1 t1, T2 t2)。最初の (違法な) コードを使用するMyClass<string, string>と、同じ関数の 2 つのインスタンスが作成されます。基本クラスでは、これらの 2 つのインスタンスは とMyClassBaseMyClassあるため、MyClass競合するのではなく、 のインスタンスが他のインスタンスを非表示にします。それがうまくいくかどうかは、あなた次第だと思います。

于 2014-03-28T19:52:15.137 に答える
0

T1この問題は、またはT2型のいずれかが別の型の子孫である (または同じ型である)場合に、実装されているメソッドのどれを呼び出す必要があるかをコンパイラが明らかにできないことが原因であると考えています。
クラスをインスタンス化するとどうなるか想像してみてくださいMyClass<int, int>

于 2014-03-28T19:55:02.507 に答える