tipo son que puede programación programacion indicado expresiones expresion delegados delegado convertir conocimientos comandos c# .net lambda delegates expression-trees

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?