Мне нужно создать динамическое выражение linq, и я начал работать со многими примерами. Я тестировал некоторые и некоторые работы, а некоторые нет. В этом случае я хочу создать метод, который выглядит так:
public bool Check(int intvar)
{
if ( i > 2 )
return true;
else
return false;
}
Теперь я написал следующее:
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);
Теперь он выбрасывает InvalidOperationException
:
Невозможно перейти к метке "label"
Что не так? Мне нужно только возвращение true или false.
Ответ 1
Вам нужно изменить несколько вещей:
-
Поместите ярлык возврата в нижней части вашей функции в блочное выражение, как предложил Рене. Вот где ваш оператор return
будет прыгать.
-
Объявите Лямбду как тип Func<int, bool>
. Поскольку вы хотите получить возвращаемое значение, это должна быть функция, а не действие.
-
Объявите метку returnTarget
как тип bool
. Поскольку возвращаемое значение блочного выражения является значением его последнего оператора, метка должна иметь правильный тип.
-
Укажите значение по умолчанию для окончательной метки (= возвращаемое значение вашей функции, если метка достигнута с помощью обычного потока управления вместо оператора 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"
Ответ 2
returnTarget
в настоящее время ссылается только на ваш оператор if/then/else. Метка не помещается в оператор в любом месте. Поэтому он не знает, куда прыгать. Метка определяется только и ссылается, но не помещается.
Попробуйте использовать Expression.Block
для объединения вашей лямбды и вашей метки.
Expression.Lambda<Action<int>>(
Expression.Block(
this.TheExpression,
Expression.Label(returnTarget)
),
new ParameterExpression[] { para }
).Compile()(5);
Не проверял, но это общее направление, в котором вы можете найти свой ответ.
-update- проверял его, лямбда выше компилируется и работает просто отлично, как она стоит сейчас.
-update2- apparantly, вы также хотите вернуть значение, позвольте мне взглянуть, по крайней мере, это должен быть Func
, а не Action
.
Ответ 3
Если у вас есть простое условие условия, как это:
if (condition)
return expression1;
else
return expression2;
Вы можете преобразовать это в троичное выражение: condition? expression1: expression2
condition? expression1: expression2
. И тогда вы можете создать выражение, не используя Label
, Return
или Goto
.
Expression condition;
Expression expression1;
Expression expression2;
/* ... */
Expression body = Expression.Condition(
test: condition,
ifTrue: expression1,
ifFalse: expression2);