Search code examples
c#loops.net-coreexpression-trees

How to create a loop expression tree


I am trying to create an expression tree that would generate this method:

public static int Print(int i) {
            int cnt = 0;
            int sum = 0;
            while (true) {
                if (cnt >= i) {
                    Console.WriteLine(sum);
                    break;
                }
                sum = sum + cnt;
                cnt++;
            }
           return sum;
        }

I do not understand how the LabelTarget works.In my case there is no value that i want to jump to...I do not understand why it does require a type.

Here is my code so far:

public static  Func<int,int> MakeExpression() {
            //input
            ParameterExpression parameter = Expression.Parameter(typeof(int));
            //define local variables and initialize them
            Expression cntVarExpr = Expression.Variable(typeof(int),"cnt");
            Expression sumVarExpr = Expression.Variable(typeof(int), "sum");

            Expression initCntExpr = Expression.Assign(cntVarExpr, Expression.Constant(0));
            Expression initSumExpr = Expression.Assign(sumVarExpr, Expression.Constant(0));

            //loop condition
            Expression condExpr = Expression.GreaterThanOrEqual(cntVarExpr, parameter);
            //block if true
            MethodInfo method = typeof(Console).GetMethod("WriteLine",new Type[] { typeof(int)});
            Expression printExpr = Expression.Call(null, method,cntVarExpr); //static method

            LabelTarget label = Expression.Label(typeof(int)); //am not sure about this one ? what
            Expression bkExpr = Expression.Break(label, sumVarExpr);
            BlockExpression block = Expression.Block(printExpr, bkExpr);

            //loop body
            Expression ifExpr = Expression.IfThen(condExpr, block);
            Expression addExpr = Expression.AddAssign(sumVarExpr,cntVarExpr);
            Expression incrExpr = Expression.Add(cntVarExpr, Expression.Constant(1));
            BlockExpression loopBodyExpr = Expression.Block(ifExpr, addExpr,incrExpr);
            LoopExpression loopExpr = Expression.Loop(loopBodyExpr);

            //method body

            Expression returnExpr = Expression.Return(label, sumVarExpr,typeof(int));

            //final expression
            BlockExpression bigExpression = Expression.Block(initCntExpr, initSumExpr, loopExpr, returnExpr);


            var meth = Expression.Lambda<Func<int, int>>(bigExpression,parameter).Compile();
            return meth;
        }

I keep getting this error:

'variable 'cnt' of type 'System.Int32' referenced from scope '', but it is not defined'

Solution

  • When you use a VariableExpression in a block, you have to pass that variable into the variables parameter of Expression.Block. This is what's causing your error.

    I'm afraid I didn't have time to go through your code in detail, but I rewrote your C# using expressions:

    var i = Expression.Parameter(typeof(int), "i");
    var cnt = Expression.Variable(typeof(int), "cnt");
    var sum = Expression.Variable(typeof(int), "sum");
    
    var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) });
    
    var breakLabel = Expression.Label("break");
    var loop = Expression.Loop(
        Expression.Block(
            Expression.IfThen(
                Expression.GreaterThanOrEqual(cnt, i),
                Expression.Block(
                    Expression.Call(writeLineMethod, Expression.Convert(sum, typeof(object))),
                    Expression.Break(breakLabel))),
            Expression.AddAssign(sum, cnt),
            Expression.PostIncrementAssign(cnt)),
        breakLabel);
    
    var block = Expression.Block(new[] { cnt, sum },
        Expression.Assign(cnt, Expression.Constant(0)),
        Expression.Assign(sum, Expression.Constant(0)),
        loop,
        sum);
    
    var method = Expression.Lambda<Func<int, int>>(block, new[] { i }).Compile();