6

私は最近これを書きました、そしてそれがコンパイルすることに驚いていました:

public class MyGeneric<U, V> {
  MyGeneric(U u) { ... }
  MyGeneric(V v) { ... }
  public void Add(U u, V v) { ... }
  public void Add(V v, U u) { ... }
}

このクラスを次のように使用すると、Addを呼び出すと、「あいまいなコンストラクター参照」と「あいまいな呼び出し」が得られます。

var myVar = new MyGeneric<int, int>(new MyIntComparer());

明らかに、intとdoubleをジェネリック型として使用する場合、もちろん両方のintを使用する場合を除いて、あいまいさはありません。両方ともdoubleに割り当てられます。

var myVar = new MyGeneric<int, double>(new MyIntComparer());
myVar.Add(3, 5);

それで、以下も許されると思いましたが、意外とエラーになりました。以下のコンパイルが許可されていないのはなぜですか?

public interface IMyInterface<T, S> {
  void Add(T t, S s);
}

public class MyGeneric<U, V> : IMyInterface<U, V>, IMyInterface<V, U> {
  public MyGeneric(U u) { }
  public MyGeneric(V v) { }
  void IMyInterface<U, V>.Add(U u, V v) { ... }
  void IMyInterface<V, U>.Add(V v, U u) { ... }
}

暗黙的または明示的なインターフェイス実装を使用するかどうかに関係なく、コンパイラは次のように述べています。

「MyGeneric<U、V>」は、「IMyInterface <U、V>」と「IMyInterface <V、U>」の両方を実装できません。これらは、一部のタイプパラメータ置換で統合される可能性があるためです。

そして、なぜ最初に書くことが許されているのですか?

4

2 に答える 2

4

1- 以下がコンパイルできないのはなぜですか?

応答の一部はその投稿にあります:なぜ C# コンパイラは、異なる基本クラスから派生したときに「型が統合される可能性がある」と不平を言うのですか?

C# 4 仕様のセクション 13.4.2 には、次のように記載されています。

ジェネリック型宣言によって実装されるインターフェイスは、可能なすべての構築型に対して一意のままでなければなりません。この規則がなければ、特定の構築された型を呼び出すための正しいメソッドを決定することは不可能です。

2-そして、なぜ最初に書くことが許されているのですか?

コンパイラは、コンパイル時にジェネリック型チェックを実行します。C# 4 仕様のセクション 7.4.3.5 には、次のように記載されています。

宣言されたシグニチャーは固有でなければなりませんが、型引数を置換すると同一のシグニチャーになる可能性があります。このような場合、上記のオーバーロード解決のタイブレーク ルールにより、最も具体的なメンバーが選択されます。次の例は、この規則に従って有効なオーバーロードと無効なオーバーロードを示しています。

interface I1<T> {...}
interface I2<T> {...}
class G1<U>
{
    int F1(U u);                    // Overload resulotion for G<int>.F1
    int F1(int i);                  // will pick non-generic
    void F2(I1<U> a);               // Valid overload
    void F2(I2<U> a);
}
class G2<U,V>
{
    void F3(U u, V v);          // Valid, but overload resolution for
    void F3(V v, U u);          // G2<int,int>.F3 will fail
    void F4(U u, I1<V> v);      // Valid, but overload resolution for   
   void F4(I1<V> v, U u);       // G2<I1<int>,int>.F4 will fail
    void F5(U u1, I1<V> v2);    // Valid overload
    void F5(V v1, U u2);
    void F6(ref U u);               // valid overload
    void F6(out V v);
}
于 2012-12-05T08:42:11.897 に答える
1

ここで受け入れられた回答で説明されているように、これは言語仕様の一部です。

C# コンパイラが、異なる基本クラスから派生したときに「型が統一される可能性がある」と文句を言うのはなぜですか?

C# 4 仕様のセクション 13.4.2 には、次のように記載されています。

型引数が L に代入された後、C から作成された可能性のある構築された型によって、L 内の 2 つのインターフェイスが同一になる場合、C の宣言は無効です。可能なすべての構築型を決定するときに、制約宣言は考慮されません。

あなたの2つの例の違いは、2番目はインターフェイスを使用することです(言語仕様に従って重複をチェックします)が、最初はタイプを使用します(ご覧のようにあいまいさを引き起こす可能性がありますが、重複をチェックしません)。

于 2012-12-05T08:51:06.893 に答える