Пример использования Expression.Block
:
/* (a, b, op) => {
int result;
switch(op)
{
case Op.Add:
result = a+b;
break;
case Op.Sub:
result = a-b;
break;
case Op.Mul:
result = a*b;
break;
case Op.Div:
result = a/b;
break;
default:
throw new ArgumentOutOfRangeException()
}
return result;
}
*/
enum Op
{
Add,
Sub,
Mul,
Div
}
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var op = Expression.Parameter(typeof(Op), "op");
var result = Expression.Parameter(typeof(int), "result");
var blockExpression = Expression.Block(
typeof(int),
new ParameterExpression[]
{
result
},
new List<Expression>()
{
Expression.Switch(op,
Expression.Throw(Expression.Constant(new ArgumentOutOfRangeException()), typeof(int)),
new[]
{
Expression.SwitchCase(Expression.Assign(result, Expression.Add(a, b)),
Expression.Constant(Op.Add)),
Expression.SwitchCase(Expression.Assign(result, Expression.Subtract(a, b)),
Expression.Constant(Op.Sub)),
Expression.SwitchCase(Expression.Assign(result, Expression.Multiply(a, b)),
Expression.Constant(Op.Mul)),
Expression.SwitchCase(Expression.Assign(result, Expression.Divide(a, b)),
Expression.Constant(Op.Div)),
}),
result
}
);
var lambda = Expression.Lambda<Func<int, int, Op, int>>(blockExpression, a, b, op);
var func = lambda.Compile();
var three = func(1, 2, Op.Add);
var negativeOne = func(1, 2, Op.Sub);
var two = func(1, 2, Op.Mul);
var @null = func(1, 2, Op.Div);
Или если сразу возвращать результат:
/* (a, b, op) => {
int result;
switch(op)
{
case Op.Add:
return a+b;
case Op.Sub:
return a-b;
case Op.Mul:
return a*b;
case Op.Div:
return a/b;
default:
throw new ArgumentOutOfRangeException()
}
}
*/
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var op = Expression.Parameter(typeof(Op), "op");
var result = Expression.Parameter(typeof(int), "result");
var returned = Expression.Label(typeof(int));
var blockExpression = Expression.Block(
new ParameterExpression[]
{
},
new List<Expression>()
{
Expression.Switch(op,
Expression.Throw(Expression.Constant(new ArgumentOutOfRangeException()),typeof(int)),
new[]
{
Expression.SwitchCase(Expression.Add(a, b),
Expression.Constant(Op.Add)),
Expression.SwitchCase(Expression.Subtract(a, b),
Expression.Constant(Op.Sub)),
Expression.SwitchCase(Expression.Multiply(a, b),
Expression.Constant(Op.Mul)),
Expression.SwitchCase(Expression.Divide(a, b),
Expression.Constant(Op.Div)),
}),
}
);
var lambda = Expression.Lambda<Func<int, int, Op, int>>(blockExpression, a, b, op);
var func = lambda.Compile();
var three = func(1, 2, Op.Add);
var negativeOne = func(1, 2, Op.Sub);
var two = func(1, 2, Op.Mul);
var @null = func(1, 2, Op.Div);
В последнем варианте SwitchExpression возвращает вычисленное значение. Stack-Base модель, последнее значение будет возвращаемым.
Можно усложнить пример, если a - нечетный, то возвращать его, если четный - считать как раньше:
static bool IsOdd(int i) => i % 2 == 1;
static void Main(string[] args)
{
/* (a, b, op) => {
int result;
switch(op)
{
case Op.Add:
return a+b;
case Op.Sub:
return a-b;
case Op.Mul:
return a*b;
case Op.Div:
return a/b;
default:
throw new ArgumentOutOfRangeException()
}
}
*/
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var op = Expression.Parameter(typeof(Op), "op");
var result = Expression.Parameter(typeof(int), "result");
var @throw = Expression.Label("throw");
var ifOdd = Expression.Label(typeof(int));
var isOddMethodInfo = ((Func<int, bool>)IsOdd).Method;
var blockExpression = Expression.Block(
typeof(int),
new ParameterExpression[]
{
},
new List<Expression>()
{
Expression.IfThenElse(
Expression.Call(null, isOddMethodInfo, a),
Expression.Return(ifOdd, a),
Expression.Return(@throw)),
Expression.Label(@throw),
Expression.Switch(op,
Expression.Throw(Expression.Constant(new ArgumentOutOfRangeException()), typeof(void)),
new[]
{
Expression.SwitchCase(Expression.Return(ifOdd,Expression.Add(a, b)), Expression.Constant(Op.Add)),
Expression.SwitchCase(Expression.Return(ifOdd,Expression.Subtract(a, b)), Expression.Constant(Op.Sub)),
Expression.SwitchCase(Expression.Return(ifOdd,Expression.Multiply(a, b)), Expression.Constant(Op.Mul)),
Expression.SwitchCase(Expression.Return(ifOdd,Expression.Divide(a, b)),
Expression.Constant(Op.Div))
}),
Expression.Label(ifOdd,Expression.Constant(0)),
}
);
var lambda = Expression.Lambda<Func<int, int, Op, int>>(blockExpression, a, b, op);
var func = lambda.Compile();
var three = func(1, 2, Op.Add); // 1
var three1 = func(2, 2, Op.Add); // 4
Expression.Label(Type type) - это метка к которой можно перейти и передать значение определенного типа (параметр type). Если число нечетное, то мы передаем значение a, иначе передаем вычисленное значение.
Expression.Return(LabelTarget, Expression value) - переход к метке LabelTarget и передача ей значения value.
Если я все правильно понял, то в Expression Tree нельзя просто вернуть значение, нужно перейти к метке, которая возвращает значение