8

私たちのプロジェクトで抱えている問題に混乱しています。効果を再現するために単純化しようとしました。

interface IBar { }

class Bar : IBar {}

interface IFoo<T> where T : IBar { }

class Foo<T> : IFoo<T> where T : IBar { }


class Class1
{
    public void DoTheFoo<T>(T bar) where T : IBar
    {}

    public void DoTheFoo<T>(IFoo<T> foo) where T : IBar
    {}


    public void Test()
    {
        var bar = new Bar();
        var foo = new Foo<Bar>();

        DoTheFoo(bar); // works

        DoTheFoo<Bar>(foo); // works
        DoTheFoo((IFoo<Bar>)foo); // works
        DoTheFoo(foo); // complains
    }
}

私にはこれは問題ないように見えますが、コンパイラは最後の呼び出しで文句を言います。これは、引数の型が適合しないのではDoTheFoo<T>(T bar)なく、を試行して文句を言うためです。DoTheFoo<T>(IFoo<T> foo)

  • メソッドを削除するDoTheFoo<T>(T bar)と、最後の呼び出しが機能します。
  • に変更するDoTheFoo<T>(Foo<T> foo)と動作しますが、使えません

現在のコードでこれを回避するのはそれほど難しくありません。しかし、a)奇妙で、b)これらの2つのオーバーロードされたメソッドを使用できないのは残念です。

この動作を説明する一般的なルールはありますか?それを機能させることは可能ですか(メソッドに異なる名前を付けることを除いて)?

4

1 に答える 1

7

過負荷の解決と組み合わせると、型推論がうまく機能しないだけの問題です。type引数を明示的に指定するだけで簡単に修正できます。キャストは必要ありません。

DoTheFoo<Bar>(foo);

通常、私はかなり異なるパラメータタイプをとるオーバーロードに神経質になっています。メソッドに異なる名前を付けるだけで、コードが単純になることがよくあります。他のことは別として、読者は型推論と同時に過負荷解決を実行しようとする必要はありません...

編集:問題は、順序付けが次のように機能することだと思います。

  • 両方の方法が見つかりました
  • 型推論は、制約を検証せずに両方のメソッドに適用されます。したがって、最初のメソッドではを取得T = Foo<Bar>し、2番目のメソッドではを取得しT = Barます。この時点では、両方の方法が適用可能です。
  • 過負荷解決が実行され、最初の方法が最も具体的な方法であると判断されます。
  • 過負荷解決が実行された後でのみ、制約がチェックされます。これは、からへTの参照変換がないため失敗します。BarIFoo

言語がこのように設計されている理由についてのEricLippertのブログ投稿、私がそれについて書いたブログ投稿、および一般的なオーバーロードについて書いた記事があります。それらのそれぞれが役立つかもしれないし、役に立たないかもしれません:)

編集:型推論をしばらく脇に置いておくと、最初の方法がより具体的である理由は、ある場合はからに変換Foo<Bar>Foo<Bar>、他の場合はからFoo<Bar>に変換するためIFoo<Bar>です。C#5仕様のセクション7.5.3.3によると、次のようになります。

式EからタイプT1に変換する暗黙の変換C1、および式EからタイプT2に変換する暗黙の変換C2が与えられた場合、次の少なくとも1つが当てはまる場合、C1はC2よりも優れた変換です。タイプがSであり、SからT1へのID変換は存在しますが、SからT2へのID変換は存在しません-..。

ID変換は、タイプからそれ自体への変換です。これ、最初のオーバーロードの場合ですが、2番目のオーバーロードの場合はそうではありません。したがって、最初の変換の方が優れています。

于 2013-03-26T14:07:22.410 に答える