表达式树练习实践:C# 循环与循环控制

内容纲要

表达式树练习实践:C# 循环

[TOC]

C# 提供了以下几种循环类型。

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for/foreach 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环 您可以在 while、for 或 do..while 循环内使用一个或多个循环。

当然,还有以下用于控制循环的语句

控制语句 描述
break 语句 终止 loopswitch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。
continue 语句 引起循环跳过主体的剩余部分,立即重新开始测试条件。

LabelTarget

LabelTarget 是用于创建循环标记的。

无论是 for 还是 while ,平时编写循环时,都需要有跳出循环的判断,有时需要某个参数自增自减并且作为判断依据。

C# 表达式树里面是没有专门表示 for /while 的,里面只有一个 Loop。看一下Loop 生成的表达式树

.Lambda #Lambda1<System.Func`1[System.Int32]>() {
    .Block(System.Int32 $x) {
        $x = 0;
        .Loop  {
            .If ($x < 10) {
                $x++
            } .Else {
                .Break #Label1 { $x }
            }
        }
        .LabelTarget #Label1:
    }
}

要实现循环控制,有 break,contauine 两种 Expression:

        public static GotoExpression Break(LabelTarget target, Type type);

        public static GotoExpression Break(LabelTarget target, Expression value);

        public static GotoExpression Break(LabelTarget target);

        public static GotoExpression Break(LabelTarget target, Expression value, Type type);
        public static GotoExpression Continue(LabelTarget target, Type type);

        public static GotoExpression Continue(LabelTarget target);

所以,要实现循环控制,必须要使用 LabelTarget,不然就无限循环了。

要理解 LabelTarget ,最好的方法是动手做。

for / while 循环

Expression.Loop 用于创建循环,包括 for 和 while,定义如下

        public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue);

      System.Linq.Expressions.LoopExpression.
        public static LoopExpression Loop(Expression body);

        public static LoopExpression Loop(Expression body, LabelTarget @break);

表达式树里面的循环,只有 Loop,无 for / while 的区别。

那么,我们来一步步理解 Loop 循环和 LabelTarget;

无限循环

                while (true)
                {
                    Console.WriteLine("无限循环");
                }

那么,对应的 Loop 重载是这种

public static LoopExpression Loop(Expression body)

使用表达式树编写

            BlockExpression _block = Expression.Block(
                new ParameterExpression[] { },
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),Expression.Constant("无限循环") )
            );

            LoopExpression _loop = Expression.Loop(_block);

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

最简单的循环

如果我想用表达式树做到如下最简单的循环,怎么写?

            while (true)
            {
                Console.WriteLine("我被执行一次就结束循环了");
                break;
            }

表达式树编写

            LabelTarget _break = Expression.Label();

            BlockExpression _block = Expression.Block(
               new ParameterExpression[] { },
               Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被执行一次就结束循环了")), Expression.Break(_break));
            LoopExpression _loop = Expression.Loop(_block, _break);

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

            Console.ReadKey();

生成的表达式树

.Lambda #Lambda1<System.Action>() {
    .Loop  {
        .Block() {
            .Call System.Console.WriteLine("我被执行一次就结束循环了");
            .Break #Label1 { }
        }
    }
    .LabelTarget #Label1:
}

首先要明确,Expression.Label() 里面可以为空,它是一种标记,不参与传递参数,不参与运算。有参无参,前后保持一致即可。

但是上面的循环只有一次,你可以将上面的标签改成这样试试 LabelTarget _break = Expression.Label(typeof(int));,原因后面找。

还有, Expression.Label() 变量需要一致,否则无法跳出。

试试一下代码

            BlockExpression _block = Expression.Block(
               new ParameterExpression[] { },
               Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被执行一次就结束循环了")), Expression.Break(Expression.Label()));
            LoopExpression _loop = Expression.Loop(_block, Expression.Label());

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

            Console.ReadKey();

里面用到了 Expression.Block(),Block() 是块,即{}。

如果 Block() 是在最外层,那么相当于是函数;如果是内嵌,相当于{};

但不是真的这样。。。表达式树里面不是完全按照 C# 的语法来还原操作的。

对于 Block() 的使用,多加实践即可。

多次循环

写一个循环十次的循环语句

            for (int i = 0; i < 10; i++)
            {
                if (i < 10)
                {
                    Console.WriteLine(i);
                }
                else
                    break;
            }

或者使用 while 表示

            int i = 0;
            while (true)
            {
                if (i < 10)
                {
                    Console.WriteLine(i);
                }
                else
                    break;
                i++;
            }

使用表达式树编写

            LabelTarget _break = Expression.Label(typeof(int));
            ParameterExpression a = Expression.Variable(typeof(int), "a");

            BlockExpression _block = Expression.Block(new ParameterExpression[] { },
                Expression.IfThenElse
                (
                    Expression.LessThan(a, Expression.Constant(10)),
                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                    Expression.Break(_break, a)
                ),
                Expression.PostIncrementAssign(a)   // a++
                );

            LoopExpression _loop = Expression.Loop(_block, _break);

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(_loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

生成的表达式树如下

.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
    .Loop  {
        .Block() {
            .If ($a < 10) {
                .Call System.Console.WriteLine($a)
            } .Else {
                .Break #Label1 { $a }
            };
            $a++
        }
    }
    .LabelTarget #Label1:
}

试试将 Expression.Break(_break, a) 改成 Expression.Break(_break)。看看报什么错。。。

解决方法是,上面的标记也改成 LabelTarget _break = Expression.Label();

就跟你写代码写注释一样,里面的东西是为了让别人看代码是容易理解。

有些同学纠结于 Expression.Label(有参或无参);Expression.Break(_break, a)Expression.Break(_break),只要看看最终生成的表达式树就清楚了。

break 和 continue 一起

C# 循环代码如下

            int i = 0;
            while (true)
            {
                if (i < 10)
                {
                    if (i % 2 == 0)
                    {
                        Console.Write("i是偶数:");
                        Console.WriteLine(i);
                        i++;
                        continue;
                    }
                    Console.WriteLine("其他任务 --");
                    Console.WriteLine("其他任务 --");
                }
                else break;
                i++;
            }

使用 C# 表达式树编写(笔者将步骤详细拆分了,所以代码比较长)

            ParameterExpression a = Expression.Variable(typeof(int), "a");

            LabelTarget _break = Expression.Label();
            LabelTarget _continue = Expression.Label();

            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶数:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            ConditionalExpression _if = Expression.IfThen(
                Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
                Expression.Block(
                    new ParameterExpression[] { },
                    Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶数:")),
                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                    Expression.PostIncrementAssign(a),
                    Expression.Continue(_continue)
                    )
                );

            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶数:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任务 --");
            //        Console.WriteLine("其他任务 --");
            BlockExpression block1 = Expression.Block(
                new ParameterExpression[] { },
                _if,
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --")),
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --"))
                );

            //    if (i < 10)
            //    {
            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶数:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任务 --");
            //        Console.WriteLine("其他任务 --");
            //    }
            //    else break;
            ConditionalExpression if_else = Expression.IfThenElse(
               Expression.LessThan(a, Expression.Constant(10)),
                block1,
                Expression.Break(_break)
                );

            //    if (i < 10)
            //    {
            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶数:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任务 --");
            //        Console.WriteLine("其他任务 --");
            //    }
            //    else break;
            //    i++ ;

            BlockExpression block2 = Expression.Block(
                new ParameterExpression[] { },
                if_else,
                Expression.PostIncrementAssign(a)
                );
            // while(true)
            LoopExpression loop = Expression.Loop(block2, _break, _continue);

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

生成的表达式树如下

.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
    .Loop .LabelTarget #Label1: {
        .Block() {
            .If ($a < 10) {
                .Block() {
                    .If (
                        $a % 2 == 0
                    ) {
                        .Block() {
                            .Call System.Console.Write("i是偶数:");
                            .Call System.Console.WriteLine($a);
                            $a++;
                            .Continue #Label1 { }
                        }
                    } .Else {
                        .Default(System.Void)
                    };
                    .Call System.Console.WriteLine("其他任务 --");
                    .Call System.Console.WriteLine("其他任务 --")
                }
            } .Else {
                .Break #Label2 { }
            };
            $a++
        }
    }
    .LabelTarget #Label2:
}

为了便于理解,上面的代码拆分了很多步。

来个简化版本

            ParameterExpression a = Expression.Variable(typeof(int), "a");

            LabelTarget _break = Expression.Label();
            LabelTarget _continue = Expression.Label();

            LoopExpression loop = Expression.Loop(
                Expression.Block(
                    new ParameterExpression[] { },
                    Expression.IfThenElse(
                        Expression.LessThan(a, Expression.Constant(10)),
                        Expression.Block(
                            new ParameterExpression[] { },
                            Expression.IfThen(
                                Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
                                Expression.Block(
                                    new ParameterExpression[] { },
                                    Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶数:")),
                                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                                    Expression.PostIncrementAssign(a),
                                    Expression.Continue(_continue)
                                    )
                                ),
                            Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --")),
                            Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --"))
                            ),
                        Expression.Break(_break)
                        ),
                    Expression.PostIncrementAssign(a)
                    ),
                _break,
                _continue
                );

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

需要注意的是,Expression.Break Expression.Continue 有所区别。

当标签实例化都是 Expression.Label() 时,

Expression.Break(label);
Expression.Continu(label);

区别在于 continu 只能用 Expression.Label()。

Break 可以这样

LabelTarget label = Expression.Label ( typeof ( int ) );
ParameterExpression a = Expression.Variable(typeof(int), "a");

Expression.Break ( label , a ) 
点赞
  1. bupropion说道:

    I am just writing to make you understand what a perfect experience my wife's princess obtained using your webblog. She noticed some things, which included what it is like to possess an excellent teaching spirit to make others with no trouble learn about specific specialized things. You undoubtedly surpassed readers' expected results. Thanks for providing those priceless, trusted, informative as well as unique tips on your topic to Mary.

  2. pamelor tablets说道:

    I am also writing to make you know what a impressive encounter my friend's girl gained reading through your web page. She learned plenty of pieces, which include what it's like to possess an amazing helping mood to let most people smoothly know several impossible subject matter. You truly exceeded our own expectations. Thank you for offering these invaluable, safe, explanatory and even fun tips about this topic to Julie.

发表评论

邮箱地址不会被公开。 必填项已用*标注

You must enable javascript to see captcha here!