C#循环与循环控制的表达式树实现

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

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

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

控制语句 描述
break 语句 终止 loop 或 switch 语句,程序流将继续执行紧接着 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 ) 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#表达式树讲解

    表达式树的概念 表达式树的创建有 Lambda法 和 组装法. 学习表达式树需要 委托.Lambda.Func<> 基础. 表达式树 形状可以参考二叉树. 可以把表达式树理解成 数学表达式. 数学表达式的所有常量.符号为表达式树的底节点.每一次计算生成的结果是一个结点,或者说他们的共同结点就是他们应该进行的运算. 生成表达式树 表达式树的创建有 Lambda表达式法 和 组装法 为了方便,这里指定生成的表达式为 ( i * j ) + ( x * y ) 他们的运算是这样的 Lambda 生成

  • C#五类运算符使用表达式树进行操作

    在 C# 中,算术运算符,有以下类型 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 这些运算符根据参数的多少,可以分作一元运算符.二元运算符.三元运算符.本文将围绕这些运算符,演示如何使用表达式树进行操作. 对于一元运算符和二元运算符的 Expression 的子类型如下: UnaryExpression; //一元运算表达式 BinaryExpression; //二元运算表达式 一,算术运算符 运算符 描述 + 把两个操作数相加 - 从第一个操作数中减去第二个操作数

  • C#用表达式树构建动态查询的方法

    前文介绍了C#中表达式树的基本知识,在实际中,表达式树有很多用法,这里举几个例子,说明如何使用表达式树构建动态查询,从而扩展LINQ的查询方法. 在LINQ中,只要数据源实现了IQuerable<T>接口,表达式树就可以用来表示结构化查询.比如,LINQ提供了用来查询关系数据源的IQueryable<T>接口的实现,C#编译器在执行这类数据源查询时,会在运行时生成表达式树,然后,查询会遍历表达式树的数据结构,然后将其转换成针对特定数据源的合适的查询语言. 下面的几个例子演示了如何使

  • C#表达式树Expression基础讲解

    什么是表达式树 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等.可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL). 这段话是来自官网( [表达式树 (C#) | Microsoft Docs](

  • C# Lambda表达式及Lambda表达式树的创建过程

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不爱说话,默默承受着编程的巨大压力,除了技术上的交流外,他们不愿意也不擅长和别人交流,更不乐意任何人走进他们的内心! 题外话说多了,咱进入正题: 上一节中,我们讲到:在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法.C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,La

  • C#判断语句的表达式树实现

    C# 提供了以下类型的判断语句: 语句 描述 if 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成. if...else 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行. 嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句. switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况. 嵌套 switch 语 您可以在一个 switch 语句内使用另一个 switch 

  • C#之Expression表达式树实例

    本文实例讲述了C#之Expression表达式树,分享给大家供大家参考.具体实现方法如下: 表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算 1.利用 Lambda 表达式创建表达式树 复制代码 代码如下: Expression<Func<int, int, int, int>> expr = (x, y, z) => (x + y) / z; 2.编译表达式树,该方法将表达式树表示的代码编译成一个可执行

  • C#表达式树的基本用法讲解

    表达式树使用一种类似树的结构来表示代码,它的每个节点都是一个表达式,比如方法调用和x<y这样的二元运算等.我们可以对表达式树的内容进行编辑和运算,这样能够动态修改可执行代码,以及动态创建查询等.我们可以使用匿名lambda表达式或者C# API来创建表达式树. 这一系列文章,主要是对C#表达式树的一种总结,基本知识参考MSDN的内容 这部分内容可以直接到MSDN上查看,后面的几篇文章主要分享一下,在工作中碰到的应用到表达式树的部分,谨做为记录和分享. 生成表达式树 通过lambda表达式创建表达

  • C#表达式树Expression动态创建表达式

    上一篇中说到了 Expression 的一些概念性东西,其实也是为了这一篇做知识准备.为了实现 EFCore 的多条件.连表查询,简化查询代码编写,也就有了这篇文章. 在一些管理后台中,对数据进行多条件查询是一件很普遍的事情,比如在用户列表需要实现可以对 "用户名"."手机号"."账户是否冻结" 等等一系列的条件查询,常见的处理方式就是通过一系列 if...else... 来对条件进行拼接.这会导致查询接口实现起来堆叠了一堆看起来有用但实际很繁琐

  • C#循环与循环控制的表达式树实现

    C# 提供了以下几种循环类型. 循环类型 描述 while 循环 当给定条件为真时,重复语句或语句组.它会在执行循环主体之前测试条件. for/foreach 循环 多次执行一个语句序列,简化管理循环变量的代码. do...while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似. 嵌套循环 您可以在 while.for 或 do..while 循环内使用一个或多个循环. 当然,还有以下用于控制循环的语句 控制语句 描述 break 语句 终止 loop 或 switch

  • C# 快速高效率复制对象(表达式树)

    1.需求 在代码中经常会遇到需要把对象复制一遍,或者把属性名相同的值复制一遍. 比如: public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class StudentSecond { public int Id { get; set; } public string Name { get; set; } p

  • C# 表达式树Expression Trees的知识梳理

    目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达式树 调试 简介 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等. 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性. 一.

  • 浅谈c#表达式树Expression简单类型比较demo

    实例如下: using System; using System.Linq.Expressions; class DynamicPredicate { public static Expression<Func<T, T, bool>> Generate<T>(string op) { ParameterExpression x = Expression.Parameter(typeof(T), "x"); ParameterExpression y

  • 利用lambda表达式树优化反射详解

    前言 本节重点不讲反射机制,而是讲lambda表达式树来替代反射中常用的获取属性和方法,来达到相同的效果但却比反射高效. 每个人都知道,用反射调用一个方法或者对属性执行SetValue和GetValue操作的时候都会比直接调用慢很多,这其中设计到CLR中内部的处理,不做深究.然而,我们在某些情况下又无法不使用反射,比如:在一个ORM框架中,你要将一个DataRow转化为一个对象,但你又不清楚该对象有什么属性,这时候你就需要写一个通用的泛型方法来处理,以下代码写得有点恶心,但不妨碍理解意思: //

  • ASP.NET Core中如何使用表达式树创建URL详解

    表达式树(Expression Tree) 表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression类.创建表达式树具体有两个优势: 1.对表达式树的代码进行编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从而达到动态切换数据库查询语句的目的,用表达式树可以动态构建针对不同数据库的查询语句. 2.完成类似反射访问未知对象的属性,通过动态构造表达式树,生成委托. 当我们在

随机推荐