この動作を理解するには、2つのことを知っておく必要があります。
- すべてのデリゲートはから派生しますが
System.Delegate
、デリゲートはタイプが異なるため、互いに割り当てることができません。
- C#言語は、メソッドまたはラムダをデリゲートに割り当てるための特別な処理を提供します。
デリゲートはタイプが異なるため、あるタイプのデリゲートを別のタイプに割り当てることはできません。
たとえば、次の場合:
delegate void test1(int i);
delegate void test2(int i);
次に:
test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a; // Using normal assignment, therefore does not compile.
上記の最初の行は、ラムダまたはメソッドをデリゲートに割り当てるための特別な処理を使用しているため、正常にコンパイルされます。
実際、この行はコンパイラーによって次のように効率的に書き換えられます。
test1 a = new test1(Console.WriteLine);
上記の2行目は、あるタイプのインスタンスを別の互換性のないタイプに割り当てようとしているため、コンパイルされません。
タイプに関しては、test1
との間には互換性のある割り当てはありませんtest2
。それらは異なるタイプだからです。
それについて考えることが役立つ場合は、次のクラス階層を検討してください。
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
次のコードは、コンパイルしていてもしませんTest1
し、Test2
同じ基本クラスから派生します:
Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.
これは、あるデリゲートタイプを別のデリゲートタイプに割り当てることができない理由を説明しています。これは通常のC#言語です。
ただし、重要なことは、互換性のあるデリゲートにメソッドまたはラムダを割り当てることが許可される理由を理解することです。上記のように、これはデリゲートのC#言語サポートの一部です。
最後にあなたの質問に答えます:
を使用Invoke()
すると、互換性のない型を割り当てようとするのではなく、メソッドまたはラムダをデリゲートに割り当てるための特別なC#言語処理を使用して、メソッド呼び出しをデリゲートに割り当てます。したがって、コンパイルはOKです。
完全に明確にするために、OPでコンパイルするコード:
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
概念的には次のように変換されます:
public test Success()
{
Func<int, int> f = x => x;
return new test(f.Invoke);
}
失敗したコードは2つの互換性のないタイプ間で割り当てを試みていますが、
public test Fail()
{
Func<int, int> f = x => x;
return f; // Attempting to assign one delegate type to another: Fails
}