5

ここで使用する正しいオーバーロードをコンパイラが解決できない理由がわかりません。(以下のコード) 適切な Add() のバージョンは 1 つだけです。BigFoo は IFoo であり、T が IFoo である IEnumerable を実装していません。しかし、それはあいまいさを報告することを主張しています。何か案は?2 番目のジェネリック型パラメーターを追加してみました - Add where T : IFoo where U : IEnumerable. ただし、過負荷は、正当な使用であっても完全に無視されます。

ジェネリック型パラメーターをキャストして指定することでこれを回避できることはわかっていますが、その時点で、オーバーロードを持つ目的を無効にしました。オーバーロードに疑問を呈することもできますが、セマンティクスは私には正しいと感じています。クラスで実装している動作は、Add() の両方がオブジェクトをホールセールとしてコレクションに個別のエントリとして追加することです。(2 番目の Add() は AddRange() であってはなりません。)

namespace NS
{
  interface IFoo { }

  class BigFoo : IFoo, IEnumerable<int>
  {
    public IEnumerator<int> GetEnumerator()
    {
      throw new NotImplementedException();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new NotImplementedException();
    }
  }

  class FooContainer
  {
    public void Add(IFoo item) { }
    public void Add<T>(IEnumerable<T> group) where T : IFoo { }
  }

  class DemoClass
  {
    void DemoMethod()
    {
      BigFoo bigFoo = new BigFoo();
      FooContainer fooContainer = new FooContainer();
      // error CS0121: The call is ambiguous between the following methods or properties: 
      // 'NS.FooContainer.Add(NS.IFoo)' and 
      // 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)'
      fooContainer.Add(bigFoo);
    }
  }
}
4

5 に答える 5

6

ジェネリック オーバーロードの解決では、制約が考慮されないため、Add<T>バージョンが適用可能であると見なされ、T=int.

IEnumerable<int>と の間に変換がないため、どちらの方法も適用できIFooます。ジェネリック メソッドは非ジェネリック メソッドよりも「特定性が低い」と見なされますが、これは、型引数の置換後にパラメーターの型が同一である場合にのみ関連しますが、この場合はそうではありません。

于 2009-06-08T15:27:06.240 に答える
0

ここでの問題は、ジェネリック型の制約がコンパイラによって完全に無視されることです (パラメーターの型のみを調べます)。コンパイラに関する限り、IEnumerable<T>渡される引数はIEnumerable<IFoo>.

この件に関する完全な情報については、C# 言語仕様のセクション25.6.4 Inference of type argumentsを参照してください。型制約の利用については言及されていないことに注意してください。

于 2009-06-08T15:40:54.697 に答える
0

BigFooコンパイラは、 がにキャストできないことを認識できるほどスマートである必要がありますIEnumerable<IFoo>が、そうではありません。それが であることを単純に認識しIEnumerable<T>、潜在的なオーバーロード候補であると感じます (定義した制約により、 であるT必要がIFooあり、intにキャストできないことが強制されますIFoo)。不便ではありますが、それほど大したことではありません。bigFoo をキャストするだけIFooで、コンパイラーは満足します:

fooContainer.Add((IFoo)bigFoo);

別の方法として、Add の一般的なオーバーロードを醜くすることもできます。

public void Add<T, U>(U group)
    where T : IFoo
    where U : IEnumerable<T>
{
}

いずれにせよ、より多くの型付けが必要です。2 番目の解決策では、呼び出しを にキャストする必要がなくなりAddますが、ジェネリック add への呼び出しで型を明示的に宣言する必要があります (最終的にはより多くのコードになります)。

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);
于 2009-06-08T15:28:56.050 に答える