Espressione dinamica di Linq con valore di ritorno

c# expression-trees

Domanda

Ho bisogno di creare un'espressione dinamica di linq e ho iniziato a lavorare con molti esempi. Ho provato alcuni e alcuni lavori e altri no. In questo caso voglio creare un metodo che assomiglia a:

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

Ora ho scritto quanto segue:

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);

Ora lancia InvalidOperationException :

Non posso saltare all'etichetta "label" `

Che c'è ? Ho solo bisogno di un ritorno vero o falso.

Risposta accettata

Devi cambiare alcune cose:

  • Metti l'etichetta di ritorno nella parte inferiore della tua funzione in un'espressione di blocco, come suggerito da Renà ©. Questo è dove la tua dichiarazione di return salterà.

  • Dichiara il Lambda come tipo Func<int, bool> . Dal momento che si desidera un valore di ritorno, questa deve essere una funzione, non un'azione.

  • Dichiara l'etichetta returnTarget come type bool . Poiché il valore di ritorno di un'espressione di blocco è il valore della sua ultima istruzione, l'etichetta deve essere del tipo corretto.

  • Fornire un valore predefinito per l'etichetta finale (= il valore di ritorno della propria funzione se l'etichetta viene raggiunta dal normale flusso di controllo anziché da un'istruzione di 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"
    

Risposta popolare

returnTarget attualmente fa riferimento solo alla tua istruzione if / then / else. L'etichetta non è collocata nell'istruzione ovunque. Quindi non sa dove saltare. L'etichetta è definita e referenziata, ma non posizionata.

Prova ad usare Expression.Block per combinare il tuo lambda e la tua etichetta.

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

Non l'ho provato, ma questa è la direzione generale in cui puoi trovare la tua risposta.

-aggiornato- testato, il lambda sopra compila e gira bene così com'è ora.

-update2- apparentemente, vuoi anche restituire un valore, fammi dare un'occhiata, almeno dovrebbe essere un Func piuttosto che Action .



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché