Skip to content

Instantly share code, notes, and snippets.

@eterekhin
Last active May 1, 2020 09:22
Show Gist options
  • Save eterekhin/92d5083482d44c6b6e0302594c731f53 to your computer and use it in GitHub Desktop.
Save eterekhin/92d5083482d44c6b6e0302594c731f53 to your computer and use it in GitHub Desktop.

Пример использования 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 нельзя просто вернуть значение, нужно перейти к метке, которая возвращает значение

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment