Expresión Linq dinámica con valor de retorno

c# expression-trees

Pregunta

Necesito crear una expresión linq dinámica y empecé a trabajar con muchos ejemplos. He probado algunos y algunos trabajos y otros no. En este caso quiero crear un método que parezca:

public bool Check(int intvar)
{
   if ( i > 2 )
     return true;
   else
     return false;
}

Ahora he escrito lo siguiente:

LabelTarget returnTarget = Expression.Label("label");
ParameterExpression para = Expression.Parameter(typeof(int), "intvalue");
Expression test = Expression.GreaterThan(para, Expression.Constant(5));
Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
Expression iffalse = Expression.Return(returnTarget,                   Expression.Constant(false));
Expression.IfThenElse(test, iftrue, iffalse);

this.TheExpression = Expression.IfThenElse(test, iftrue, iffalse);
Expression.Lambda<Action<int>>(
this.TheExpression,
new ParameterExpression[] { para }
).Compile()(5);

Ahora lanza InvalidOperationException :

No se puede saltar a la etiqueta "etiqueta" `

Qué está mal ? Solo necesito una devolución verdadera o falsa.

Respuesta aceptada

Necesitas cambiar algunas cosas:

  • Coloque la etiqueta de retorno en la parte inferior de su función en una expresión de bloque, como sugirió René. Aquí es donde su declaración de return saltará.

  • Declare el Lambda como tipo Func<int, bool> . Dado que desea un valor de retorno, esto debe ser una función, no una acción.

  • Declare la etiqueta returnTarget como tipo bool . Dado que el valor de retorno de una expresión de bloque es el valor de su última declaración, la etiqueta debe ser del tipo correcto.

  • Proporcione un valor predeterminado para la etiqueta final (= el valor de retorno de su función si el flujo de control normal alcanza la etiqueta en lugar de una declaración de return ).

    LabelTarget returnTarget = Expression.Label(typeof(bool));
    ParameterExpression para = Expression.Parameter(typeof(int), "intvalue");
    Expression test = Expression.GreaterThan(para, Expression.Constant(5));
    Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
    Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false));
    
    var ex = Expression.Block(
        Expression.IfThenElse(test, iftrue, iffalse),
        Expression.Label(returnTarget, Expression.Constant(false)));
    
    var compiled = Expression.Lambda<Func<int, bool>>(
        ex,
        new ParameterExpression[] { para }
    ).Compile();
    
    Console.WriteLine(compiled(5));     // prints "False"
    Console.WriteLine(compiled(6));     // prints "True"
    

Respuesta popular

returnTarget actualmente solo es referenciado por su sentencia if / then / else. La etiqueta no se coloca en la declaración en ningún lugar. Así que no sabe a dónde saltar. La etiqueta solo se define y hace referencia, pero no se coloca.

Intente usar Expression.Block para combinar su lambda y su etiqueta.

Expression.Lambda<Action<int>>(
    Expression.Block(
        this.TheExpression,
        Expression.Label(returnTarget)
    ),
    new ParameterExpression[] { para }
    ).Compile()(5);

No lo he probado, pero esta es la dirección general en la que puedes encontrar tu respuesta.

-update- probado, la lambda de arriba compila y funciona bien tal como está ahora.

-update2- aparentemente, también quieres devolver un valor, déjame echarle un vistazo, al menos, debería ser un Func lugar de una Action .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow