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


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

    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 ) 
    
  • 相关阅读:
    【剑指offer】字符串转整数
    怎样让js不产生冲突,避免全局变量的泛滥,合理运用命名空间
    [每天一个知识点]34-职业生涯-用得着和用不着的知识
    真机iOS SDK升级后xcode不能进行真机调试 怎么办
    SPOJ 11840. Sum of Squares with Segment Tree (线段树,区间更新)
    Atitit.Gui控件and面板----web server区----- web服务器监控面板and控制台条目
    Struts2+Spring+Hibernate step by step 03 整合Spring之中的一个(在DAO层验证username和password)
    WPF中控件ListView和DataGrid典型属性介绍
    leetcode
    layer:好看的弹出窗口
  • 原文地址:https://www.cnblogs.com/whuanle/p/11559795.html
Copyright © 2020-2023  润新知