Есть две вещи, которые вы должны знать, чтобы понять это поведение.
- Все делегаты являются производными
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
}