Для понимания этого поведения необходимо знать две вещи.
- Все делегаты являются производными от
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.
Первая строка выше компилирует OK, поскольку она использует специальную обработку для присвоения лямбды или метод для делегата.
Фактически эта строка эффективно переписывается компилятором следующим образом:
test1 a = new test1(Console.WriteLine);
Вторая строка выше не компилируется, поскольку она пытается назначить экземпляр один тип другому несовместимый тип.
Что касается типов go, то нет совместимого назначения между test1
и test2
, поскольку они являются разными типами.
Если это помогает чтобы подумать об этом, рассмотрим эту иерархию классов:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
Следующий код НЕ будет компилироваться, даже если Test1
и Test2
являются производными от одного базового класса:
Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.
Это объясняет, почему вы не можете назначить один тип делегата другому. Это просто обычный C# язык.
Однако важно понять, почему вы можете назначить метод или лямбду совместимому делегату. Как отмечалось выше, это является частью языковой поддержки C# для делегатов.
Итак, наконец, чтобы ответить на ваш вопрос:
Когда вы используете Invoke()
, вы назначаете вызов METHOD для делегировать, используя специальную обработку языка C# для назначения методов или лямбда-выражений делегату, а не пытаться назначить несовместимый тип - следовательно, он компилируется.
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);
}
В то время как код ошибки пытается присвоить два несовместимых типа:
public test Fail()
{
Func<int, int> f = x => x;
return f; // Attempting to assign one delegate type to another: Fails
}