表达式树练习实践: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.BreakExpression.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 )
相关文章
|
编译器 C#
表达式树练习实践:C#值类型、 引用类型、泛型、集合、调用函数
表达式树练习实践:C#值类型、 引用类型、泛型、集合、调用函数
138 0
表达式树练习实践:C#判断语句
表达式树练习实践:C#判断语句
119 0
表达式树练习实践:C# 五类类运算符的表达式树表达
表达式树练习实践:C# 五类类运算符的表达式树表达
98 0
|
算法 C#
【愚公系列】2021年11月 C#版 数据结构与算法解析(树)
【愚公系列】2021年11月 C#版 数据结构与算法解析(树)
100 0
|
机器学习/深度学习 C# 算法
带你读《C#神经网络编程》之三:决策树和随机森林
本书旨在为C#程序员使用神经网络、CNTK等C#库和TensorFlowSharp解决复杂的计算问题时,提供实践指导。本书从神经网络入门知识开始,详细介绍如何使用Encog、Aforge和Accord搭建一个神经网络,帮助你深入理解神经网络相关概念和技术,例如深度网络、感知器、优化算法、卷积网络和自动解码器。此外,还详细讲解如何向.NET应用程序中添加智能特性,例如面部和运动检测、对象检测和标注、语言理解、知识和智能搜索。
|
API C#
(C#)把磁盘目录树加载在窗体菜单中
这又是一个没有技术含量的代码。写出来只是玩玩,所以也不敢放在首页。这里有个问题,是获取文件/文件夹的图标。使用 System.Drawing.Icon.ExtractAssociatedIcon 只能获取大图标(不知道有没有高手能做到取小图标)。
924 0