No entiendo este caso:
public delegate int test(int i);
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
public test Fail()
{
Func<int, int> f = x => x;
return f; // <- code doesn't compile
}
¿Por qué la compilación está bien cuando uso el Invoke
método y no está bien cuando regreso csharp Func<int,int>
directamente?
delegate void test1(int i);
ydelegate void test2(int i);
Respuestas:
Hay dos cosas que necesita saber para comprender este comportamiento.
System.Delegate
, pero diferentes delegados tienen diferentes tipos y, por lo tanto, no pueden asignarse entre sí.Debido a que diferentes delegados tienen diferentes tipos, eso significa que no puede asignar un delegado de un tipo a otro.
Por ejemplo, dado:
Entonces:
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:
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
ytest2
porque son tipos diferentes.Si ayuda pensarlo, considere esta jerarquía de clases:
El siguiente código no se compilará, a pesar de que
Test1
yTest2
derivar de la misma clase base: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:
En realidad se convierte conceptualmente en algo como:
Mientras que el código que falla intenta asignar entre dos tipos incompatibles:
fuente
En el segundo caso,
f
es de tipoFunc<int, int>
, pero se dice que el método devuelve atest
. Estos son tipos no relacionados (delegados), que no son convertibles entre sí, por lo que se produce un error del compilador. Puede ir a esta sección de las especificaciones de idioma y buscar "delegar". No encontrará ninguna mención de conversiones entre delegados que tengan las mismas firmas.Sin embargo, en el primer caso,
f.Invoke
es una expresión de grupo de método , que en realidad no tiene un tipo. El compilador de C # convertirá expresiones de grupos de métodos a tipos de delegados específicos según el contexto, a través de una conversión de grupo de métodos .(Citando la quinta viñeta aquí , énfasis mío)
En este caso, se convierte al
test
tipo de delegado.En otras palabras,
return f
no funciona porquef
ya tiene un tipo, perof.Invoke
aún no lo tiene.fuente
El problema aquí es la compatibilidad de tipos:
La siguiente es la definición de delegado de Func de las fuentes de MSDN:
public delegate TResult Func<in T, out TResult>(T arg);
Si ve que no hay una relación directa entre el Func mencionado anteriormente y su Delegado definido:
public delegate int test(int i);
Los delegados se comparan usando la firma, que es parámetros de entrada y resultado de salida, en última instancia, un delegado es un puntero de función y dos funciones se pueden comparar solo a través de la firma. En tiempo de ejecución, el método invocado a través de Func se asigna al
Test
delegado, ya que Signature es el mismo y funciona a la perfección. Es una asignación de puntero de función, dondeTest
delegado ahora invocará el método señalado por el delegado de FuncEntre Func y el delegado de prueba, no hay compatibilidad de tipo / asignación, Func no puede completar como parte de las reglas del sistema Tipo. Incluso cuando su resultado se puede asignar y completar
test delegate
como se hizo en el primer caso.fuente