Hay dos cosas que necesita saber para comprender este comportamiento.
- Todos los delegados se derivan
System.Delegate
, pero diferentes delegados tienen diferentes tipos y, por lo tanto, no pueden asignarse entre sí.
- El lenguaje C # proporciona un manejo especial para asignar un método o lambda a un delegado .
Debido a que diferentes delegados tienen diferentes tipos, eso significa que no puede asignar un delegado de un tipo a otro.
Por ejemplo, dado:
delegate void test1(int i);
delegate void test2(int i);
Entonces:
test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a; // Using normal assignment, therefore does not compile.
La primera línea anterior compila OK porque está usando el manejo especial para asignar una lambda o un método a un delegado.
De hecho, esta línea es efectivamente reescrita así por el compilador:
test1 a = new test1(Console.WriteLine);
La segunda línea anterior no se compila porque está intentando asignar una instancia de un tipo a otro tipo incompatible.
En cuanto a los tipos, no hay una asignación compatible entre test1
y test2
porque son tipos diferentes.
Si ayuda pensarlo, considere esta jerarquía de clases:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
El siguiente código no se compilará, a pesar de que Test1
y Test2
derivar de la misma clase base:
Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.
Esto explica por qué no puede asignar un tipo de delegado a otro. Ese es solo el lenguaje normal de C #.
Sin embargo, lo crucial es entender por qué se le permite asignar un método o lambda a un delegado compatible. Como se señaló anteriormente, esto es parte del soporte de lenguaje C # para los delegados.
Así que finalmente para responder tu pregunta:
Cuando lo usa Invoke()
, está asignando una llamada de MÉTODO al delegado usando el manejo especial del lenguaje C # para asignar métodos o lambdas a un delegado en lugar de intentar asignar un tipo incompatible, por lo tanto, se compila OK.
Para ser completamente claro, el código que compila en su OP:
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
En realidad se convierte conceptualmente en algo como:
public test Success()
{
Func<int, int> f = x => x;
return new test(f.Invoke);
}
Mientras que el código que falla intenta asignar entre dos tipos incompatibles:
public test Fail()
{
Func<int, int> f = x => x;
return f; // Attempting to assign one delegate type to another: Fails
}