2

コンパイラの理解できない動作に少し困惑しています。次のコード サンプルに減らしました。

    public interface IFoo { }
    public interface IBar<T> : IFoo { }
    public delegate void DHandler<T>(IBar<T> arg);

    public static class Demo
    {
        static void Garply<T>(DHandler<T> handler) { }

        public static void DoStuffWithInt()
        {
            Garply<int>(Handler);
        }

        static void Handler(IFoo arg) { }
    }

私の問題は、コードがコンパイルされるとは思っていませんが、コンパイルされることです。署名がDHandler<int>必要なため、コンパイルすることは期待していませんが、メソッドは を宣言していますが、そうではありません(逆は真です)。したがって、 は ではないため、デリゲートを呼び出しの引数として使用することはできません。IBar<int>HandlerIFoo IBar<int>HandlerDHandler<int>Garply<int>

コードを変更して読み取るHandler(IBar<int> arg)と、コンパイルされます。読むように変更しても、Handler(IBar<string> arg)そうではありません。これらの動作は両方とも、私が期待するとおりです。

この質問を促す実際的な問題は、署名が の場合、呼び出しHandler(IBar<int> arg)の型パラメーターを明示的に指定する必要があるとコンパイラーが不平を言うことです。Garplyこの例では些細なことですが、実際のコードでは非常に厄介です。への引数Garplyは署名付きのメソッドである(IBar<int> arg)ため、それへのデリゲートは になりDHandler<int>、選択されたGarply<T>は明確に になるため、私は戸惑いましたGarply<int>。しかし、どうやらコンパイラはあいまいさを見ました。IFooそれが私を上記のパズルに導いたことを調査していましIBar<T>た.おそらくコンパイラーが考えていると推測Tできます.私はそれを次のようにコンパイルする必要がありますIBar<T>「としてではなくIFoo、型パラメーターが必要な理由を説明するかもしれません。しかし、誰でもこれに光を当てることができますか?

4

1 に答える 1

8

これは、C# 2 で導入されたデリゲート バリアンスです。これは、C# 4 で導入された汎用バリアンスと同じではありません。

さらに簡単な例を次に示します。

delegate void Foo(string x);    

class Test
{
    static void Main()
    {
        Foo foo = Bar;
    }

    static void Bar(object y) {}
}

ポイントは、メソッドからFooデリゲートのインスタンスを作成できることです。デリゲートが呼び出されると、常に参照が提供され、からへの参照変換が行われます。だから私が持っている場合:BarBar objectFoostringstringobject

Foo f = ...;
f("fred");

... その呼び出しは常にに適していBarます。

同様に、あなたの場合、Garply<T>withの呼び出しhandler間違いなく有効な呼び出しにHandlerなるため、コンパイラは適切なDHandler<T>インスタンスを喜んで作成します。

Handlerのみを受け入れる場合の問題IBar<int>は、型パラメーターを推論するときに、コンパイラーが引数に対して可能なメソッドグループ変換を使用しないことです。これは、型推論が確かに強力になる可能性のある領域であり、実際、C# 3 と C# 4 の間で、非常によく似た領域で (詳細は思い出せませんが) 改善されました。

于 2012-12-16T21:23:11.243 に答える