c# - son - no se puede convertir expresion lambda en el tipo delegado indicado
Árboles de expresión e invocación de un delegado (4)
Así que tengo un delegate
que apunta a una función que no conozco realmente cuando creo por primera vez el objeto delegate
. El objeto se establece en alguna función más tarde.
También quiero hacer un árbol de expresiones que invoque al delegado con un argumento (para esta pregunta, el argumento puede ser 5
). Esta es la parte con la que estoy luchando; El código de abajo muestra lo que quiero pero no compila.
Func<int, int> func = null;
Expression expr = Expression.Invoke(func, Expression.Constant(5));
Para este ejemplo que podría hacer (esto es práctico ya que necesito construir los árboles de expresión en tiempo de ejecución):
Func<int, int> func = null;
Expression<Func<int>> expr = () => func(5);
Esto hace que expr
convierta en:
() => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5)
Lo que parece significar que para usar la func
delegate
, necesito producir el value(Test.Program+<>c__DisplayClass0).func
bit.
Entonces, ¿cómo puedo hacer un árbol de expresiones que invoque a un delegado?
Creo que lo que quieres hacer es usar las propiedades de Destino y Método del delegado para pasar y crear una expresión de llamada. Sobre la base de la muestra de JulianR, este es el aspecto que tendría:
Action<int> func = i => Console.WriteLine(i * i);
var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5));
var lambdaExpr = Expression.Lambda<Action>(callExpr);
var fn = lambdaExpr.Compile();
fn(); // Prints 25
Esto debería funcionar:
Action<int> func = i => Console.WriteLine(i * i);
// If func is null like in your example, the GetType() call fails,
// so give it a body or use typeof if you know the type at compile time
var param = Expression.Parameter(func.GetType());
// Call the Invoke method on the delegate, which is the same as invoking() it
var callExpr = Expression.Call(param, func.GetType().GetMethod("Invoke"), Expression.Constant(5));
var lambdaExpr = Expression.Lambda<Action<Action<int>>>(callExpr, param);
var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed
fn(func); // Prints 25
Las expresiones pueden ser divertidas, pero recuerda: las expresiones siempre se construyen a partir de otras expresiones . Una expresión es un árbol de otras expresiones que describe el código. No puede pasar el delegado real como lo hace en su ejemplo, lo que necesita es una expresión de ese delegado, diciendo que la expresión espera un parámetro del tipo de su delegado. Luego dice que desea llamar a un método en ese parámetro, a saber, el método Invoke
, con el argumento ''5''. Todo lo demás después de eso es solo si desea convertir la expresión en un código ejecutable, lo que probablemente haga.
Sin embargo, corrí esto con .NET4, espero no haber mezclado solo las expresiones de .NET4.
EDITAR En respuesta al comentario de PythonPower:
Creo que lo que quieres (no pasar al delegado como un argumento) solo se puede hacer cuando el delegado mismo se describe como una expresión, como esta:
var arg = Expression.Parameter(typeof(int), "i");
var multiply = Expression.Multiply(arg, arg);
var writeln = Expression.Call(typeof(Console).GetMethod("WriteLine",
new[] { typeof(int) }), multiply);
var lambda = Expression.Lambda<Action<int>>(writeln, arg);
var compiled = lambda.Compile();
compiled(5); // Prints 25
La única otra forma en que puedo pensar es capturar a un delegado declarado localmente en un cierre, pero no sabría cómo hacerlo.
Mientras que otras respuestas proporcionan algunas formas de trabajo, hay una más corta:
Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)
Funciona tanto para delegados que hacen referencia a métodos estáticos como a métodos de instancia sin cambios.
OK, esto muestra cómo se puede hacer (pero en mi opinión es muy poco elegante):
Func<int, int> func = null;
Expression<Func<int, int>> bind = (x) => func(x);
Expression expr = Expression.Invoke(bind, Expression.Constant(5));
Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(expr);
Func<int> compiled = lambda.Compile();
Console.WriteLine(expr);
func = x => 3 * x;
Console.WriteLine(compiled());
func = x => 7 * x;
Console.WriteLine(compiled());
Console.Read();
Esencialmente uso (x) => func(x);
para hacer una función que llame a lo que apunta el delegado. Pero puedes ver que expr
es demasiado complicado. Por esta razón, no considero que esta respuesta sea buena, pero ¿tal vez se pueda desarrollar?