c# - que - Auto anulación de funciones anónimas.
llamar funcion c# (2)
En JavaScript, no es raro ver las funciones de auto-invocación:
var i = (function(x) {
return x;
})(42);
// i == 42
Si bien ciertamente no estoy comparando los idiomas, pensé que tal construcción sería traducible a C #, siempre que haya una versión en idioma con soporte:
var i = (delegate(int x) {
return x;
})(42);
O:
var i = ((x) => {
return x;
})(42);
O incluso:
var i = (x => x)(42);
Sin embargo, cada versión está en error:
Nombre del método esperado
¿No se admiten los métodos anónimos de auto-invocación ( debido a una prohibición explícita, o la imposibilidad de inferencia de tipo ), o hay un error en mis intentos?
Estoy aventurándome a adivinar que debido a que no había ninguna declaración de método ( Func<T,T>
) contra la cual se pudieran inferir los tipos, no se pueden ordenar los tipos implícitos, cifras que pretendía llamar un método declarado por nombre, Y realmente ridiculizó la sintaxis.
Errata
Antes de esta pregunta se inunda con:
var i = new Func<int, int>(x => x)(42);
Debo decir que esperaba aprovechar la inferencia de tipos, pero supongo que puede que no sea posible debido a que es demasiado implícito.
Entonces, aclarando un poco la pregunta; sabemos que podemos var i = new Func<int, int>(x => x)(42);
pero sin crear una instancia de, o convertir a Func
, ¿es esto posible?
Caso de uso
Para aquellos curiosos ( o preocupados ) el caso de uso fue algo como esto:
var o = new {
PropertyA = () => {
// value computation
}(),
PropertyB = () => {
// value computation
}(),
};
Supongo que alguien del equipo de C # puede dar una respuesta mucho mejor, pero intento mi disparo. La pregunta aquí no es si es una buena práctica o no, sino si es posible y, si no, por qué.
Empecemos con lo que escribirías:
var i = (x => x)(42);
Bastante sencillo, pasa un entero ( 42
) a un lambda, juega con él y devuelve el mismo valor. Su parámetro es un entero (deducido de su uso), devuelve un entero (inferido de la expresión) e i
es otro entero (inferido del valor de retorno).
Desafortunadamente esto se rompe en el primer paso. Permítanme citar al gran Jon :
El compilador intenta calcular los tipos de parámetros para las expresiones lambda según el contexto en el que se utilizan.
Qué significa eso? En realidad, el compilador necesita algo de información, pero solo puede usar:
- Una conversión explícita, constructor o declaración como en
(Func<int, int>)(x => x))
onew Func<int, int>(x => x)
. Los tipos requeridos aquí se dan o se deducen utilizando el tipo final requerido. - Una asignación (de nuevo porque el tipo final se puede inferir) como en:
Func<int, int> f = (x => x);
En su caso, el compilador tiene que inferir el tipo de función de sus parámetros y no es posible porque los parámetros se validan con la expresión y no al revés.
¿Por qué esto no es posible en C #? Debido a que (x => x)
es solo un delegado, entonces puede ser cualquier delegado que acepte un parámetro entero (si suponemos que el compilador puede inferir lhs de rhs y validar rhs según lhs) y devolver otro entero. En realidad, la conversión entre delegados no está permitida en C #, por lo que la expresión no es válida (incluso si este delegado especial no se asignará a ninguna variable y no se utilizará en el futuro). Puede ser Func<int, int>
o Expression<Func<int, int>>
o cualquier otro delegado (para el parámetro bool
puede ser incluso Predicate<bool>
).
Por supuesto, esta es una decisión de diseño de C #, la misma expresión, por ejemplo, en VB.NET es perfectamente válida:
Dim i = (Function(x) x)(42)
Lenguaje diferente, reglas diferentes a las que obedecer, la meta de C # es evitar este tipo de ambigüedad.
var x = ((Func<int, int>)(y => y * 2))(10);
El problema es que cuando el compilador ve y => y * 2
lo clasifica como una Expression
lugar de un Func
de forma predeterminada, a menos que haya información contextual que le permita saber que debería ser un Func
. Al asignarlo a Func
le damos el contexto que necesita.