Есть две вещи, которые вы должны знать, чтобы понять это поведение.
- Все делегаты являются производными
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);
Вторая строка выше не компилируется, потому что она пытается присвоить экземпляр одного типа другому несовместимому типу.
Что касается типов, не существует совместимого назначения между 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 # для назначения методов или лямбда-выражений делегату, а не пытаетесь назначить несовместимый тип - следовательно, он компилируется 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);
}
Принимая во внимание, что код ошибки пытается назначить между двумя несовместимыми типами:
public test Fail()
{
Func<int, int> f = x => x;
return f; // Attempting to assign one delegate type to another: Fails
}