2012-12-03 12 views
9

Potrzebuję stworzyć dynamiczną ekspresję linq i zacząłem pracę z wieloma przykładami. Testowałem niektóre i niektóre prace, a niektóre nie. W tym przypadku chcę stworzyć metodę, która wygląda tak:Dynamiczne wyrażenie Linq z wartością zwracaną

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

Teraz Pisałem następujące:

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

Teraz rzuca InvalidOperationException:

nie może przeskoczyć do etykiety "etykieta" `

Co jest nie tak? Potrzebuję tylko zwrotu prawda lub fałsz.

+0

Czy możesz powiedzieć nam coś więcej o tym, co staramy się osiągnąć? Na przykład, dlaczego trzeba dynamicznie tworzyć to wyrażenie ... i dlaczego trzeba używać etykiet, a jeśli \ else, gdy można napisać: public bool Check (int intvar) { return i> 2; } –

Odpowiedz

14

trzeba zmienić kilka rzeczy:

  • Umieść etykietę zwrotną na dnie swojej funkcji w wyrażeniu bloku, jak sugeruje René. Tutaj przeskoczy twoja instrukcja return.

  • Zadeklaruj Lambdę jako typ Func<int, bool>. Ponieważ chcesz uzyskać wartość zwracaną, musi to być funkcja, a nie akcja.

  • Należy zadeklarować etykietę returnTarget jako typ bool. Ponieważ zwracana wartość wyrażenia blokowego jest wartością ostatniej instrukcji, etykieta musi być poprawnego typu.

  • Podaj wartość domyślną dla etykiety końcowej (= wartość zwracana przez funkcję, jeśli etykieta jest osiągnięta przez normalny przepływ sterowania zamiast instrukcji 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" 
    
+1

Dzięki. Świetna robota. Więc operator goto się odradza. ;-). – sven

+6

Jeśli spojrzysz na skompilowany kod IL, zobaczysz, że nigdy tak naprawdę nie zostało ...;) –

1

returnTarget obecnie jest przywoływana tylko przez instrukcję if/then/else. Etykieta nie jest nigdzie umieszczana w zestawieniu. Więc nie wie, dokąd się przeskoczyć. Etykieta jest zdefiniowana i przywoływana, ale nie umieszczona.

Użyj kombinacji Expression.Block, aby połączyć swoją lambdę z etykietą.

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

Nie testowałem tego, ale jest to ogólny kierunek, w którym można znaleźć odpowiedź.

-update- przetestował to, powyższa Lambda się kompiluje i działa dobrze, jak teraz.

-update2-odpowiednio, chcesz również zwrócić wartość, pozwól mi spojrzeć, przynajmniej powinien to być Func zamiast Action.

Powiązane problemy