11

更新: C# 7.3 以降、これは問題ではなくなりました。リリースノートから:

メソッド グループに、型引数が制約を満たさないジェネリック メソッドが含まれている場合、これらのメンバーは候補セットから削除されます。

C# 7.3 以前:

だから私はEric Lippert の 'Constraints are not part of the signature'を読み、オーバーロードの解決後に型制約がチェックされることを仕様が指定していることを理解しましたが、なぜこれがそうでなければならないのかについてはまだ明確ではありません。以下はエリックの例です。

static void Foo<T>(T t) where T : Reptile { }
static void Foo(Animal animal) { }
static void Main() 
{ 
    Foo(new Giraffe()); 
}

オーバーロードの解決 for: が最適なオーバーロード マッチであると推測するため、これはコンパイルされませんFoo(new Giraffe())Foo<Giraffe>、型制約が失敗し、コンパイル時エラーがスローされます。エリックの言葉で:

ここでの原則は、オーバーロードの解決 (およびメソッドの型の推論) であり、引数のリストと各候補メソッドの仮パラメーターのリストとの間で可能な限り一致するものを見つけます。つまり、候補メソッドのシグネチャを調べます。

型制約は署名の一部ではありませんが、なぜできないのでしょうか? 型制約を署名の一部と見なすのが悪い考えであるシナリオには、どのようなものがありますか? 実装が難しい、または不可能ですか?最適なオーバーロードが何らかの理由で呼び出すことができない場合は、2 番目に最適なオーバーロードに静かにフォールバックすることを推奨しているわけではありません。私はそれを嫌います。最適なオーバーロードの選択に影響を与えるために型制約を使用できない理由を理解しようとしています。

私は、C# コンパイラの内部で、オーバーロードの解決のみを目的として (メソッドを完全に書き換えるわけではありません)、次のことを想像しています。

static void Foo<T>(T t) where T : Reptile { }

次のように変換されます:

static void Foo(Reptile  t) { }

型制約を仮パラメータ リストに「引き込む」ことができないのはなぜですか? これはどのように署名を悪い方法で変更しますか? 署名を強化するだけのような気がします。次にFoo<Reptile>、過負荷候補と見なされることはありません。

編集 2:私の質問がとても混乱していたのも不思議ではありません。私は Eric のブログを正しく読んでおらず、間違った例を引用しました。より適切だと思う例を編集しました。また、タイトルをより具体的なものに変更しました。この質問は、最初に想像したほど単純ではないように思えます。おそらく、いくつかの重要な概念が欠けています。これがスタックオーバーフローの資料であるかどうかはわかりません。この質問/ディスカッションを他の場所に移動するのが最善かもしれません.

4

2 に答える 2

6

C# コンパイラは、CLR のメソッド シグネチャの一部ではないため、型制約をメソッド シグネチャの一部と見なす必要はありません。オーバーロードの解決が言語ごとに異なる場合は悲惨です (主に、実行時に発生する可能性があり、言語ごとに異なるべきではない動的バインディングが原因です。さもないと、すべての地獄が解き放たれます)。

これらの制約が CLR のメソッド シグネチャの一部ではないことが決定された理由は、まったく別の問題であり、それについては十分な情報に基づいていない推測しかできませんでした。詳しい人に答えてもらいます。

于 2012-02-26T02:07:29.730 に答える
0

が複数の制約に一致する場合T、自動的に解決できないあいまいさが生じます。たとえば、制約を持つ1つのジェネリッククラスがあります

where T : IFirst

もう1つは制約付き

where T : ISecond

T を と の両方を実装するクラスにする必要がIFirstありISecondます。

具体的なコード例:

public interface IFirst
{
    void F();
}

public interface ISecond
{
    void S();
}

// Should the compiler pick this "overload"?
public class My<T> where T : IFirst
{
}

// Or this one?
public class My<T> where T : ISecond
{
}

public class Foo : IFirst, ISecond
{
    public void Bar()
    {
        My<Foo> myFoo = new My<Foo>();
    }

    public void F() { }
    public void S() { }
}
于 2012-02-25T03:28:14.953 に答える