表达式树练习实践:C# 循环与循环控制
表达式树练习实践: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 )
表达式树练习实践:C# 循环与循环控制的更多相关文章
- 表达式树练习实践:C# 五类运算符的表达式树表达
目录 表达式树练习实践:C# 运算符 一,算术运算符 + 与 Add() - 与 Subtract() 乘除.取模 自增自减 二,关系运算符 ==.!=.>.<.>=.<= 三 ...
- 表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数
目录 表达式树练习实践:C#值类型.引用类型.泛型.集合.调用函数 一,定义变量 二,访问变量/类型的属性字段和方法 1. 访问属性 2. 调用函数 三,实例化引用类型 四,实例化泛型类型于调用 五, ...
- 表达式树练习实践:C#判断语句
目录 表达式树练习实践:C#判断语句 if if...else switch ?? 和 ?: 表达式树练习实践:C#判断语句 判断语句 C# 提供了以下类型的判断语句: 语句 描述 if 一个 if ...
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...
- 干货!表达式树解析"框架"(1)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 关于我和表达式树 其实我也没有深入了解表达式树一些内在实现的原理 ...
- C# Lambda表达式详解,及Lambda表达式树的创建
最近由于项目需要,刚刚学完了Action委托和Func<T>委托,发现学完了委托就必须学习lambda表达式,委托和Lambda表达式联合起来,才能充分的体现委托的便利.才能使代码更加简介 ...
- 转帖:用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树 这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托 ...
- LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树
序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...
- 16.C#初见Lambda表达式及表达式树(九章9.1-9.3)
在说明Lambda相关知识前,我们需要了解Lambda表达式常用于LINQ,那么我们来聊下LINQ. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑: ...
随机推荐
- 入门MySQL——查询语法练习
前言: 前面几篇文章为大家介绍了DML以及DDL语句的使用方法,本篇文章将主要讲述常用的查询语法.其实MySQL官网给出了多个示例数据库供大家实用查询,下面我们以最常用的员工示例数据库为准,详细介绍各 ...
- Jenkins将ASP.NETCore部署到Azure
首先需要获得Azure上App-service 的porfile. 登录portal 选到app,点击Get publish pofile 将得到一个 ****.PublishSettings,注意这 ...
- Zabbix数据库空间大小使用计算
一.Zabbix的数据存储主要分类 1.历史数据 2.趋势数据 3.事件数据 二.每秒处理的数据量 顾名思义,例如,有3000个监控项(item),每60秒取一次值,即平均每秒有50(3000/60) ...
- 基于注解的SpringAOP源码解析(三)
注意,读完本篇文章需要很长很长时间 在之前的2篇文章:AOP源码分析(一)AOP源码分析(二) 中,我们搭建了SpringAOP源码分析的环境,介绍了@EnableAspectJAutoProxy注解 ...
- Android进阶之绘制-自定义View完全掌握(五)
在自定义类继承View实现自定义控件的过程中,我们还应该对一些自定义属性有所了解. 我们通过一个案例来学习一下. 新建一个android项目,然后我们创建一个类MyAttributeView继承Vie ...
- 牛客网2016.4.11(两个数相加为sum/计数一个int型的二进制有多少个1/二叉树是否左右对称)
求最小的两个数相加为sum //求最小的两个数相加为sum public ArrayList<Integer> FindNumbersWithSum(int [] array,int su ...
- 深入浅出TypeScript(2)- 用TypeScript创建web项目
前言 在第一篇中,我们简单介绍了TypeScript的一些简单语法,那么如果我们只是简单使用TypeScript开发一个web项目,应该做哪些准备?接下来我们就结合TypeScript和Webpack ...
- 新手学习FFmpeg - 调用API完成录屏
调用FFMPEG Device API完成Mac录屏功能. 调用FFMPEG提供的API来完成录屏功能,大致的思路是: 打开输入设备. 打开输出设备. 从输入设备读取视频流,然后经过解码->编码 ...
- Leetcode之深度优先搜索(DFS)专题-515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)
Leetcode之深度优先搜索(DFS)专题-515. 在每个树行中找最大值(Find Largest Value in Each Tree Row) 深度优先搜索的解题详细介绍,点击 您需要在二叉树 ...
- React项目升级遇到的问题复盘(2019-09-02)
老铁们,发没发现我换了个贼帅的头像,高端大气上档次,非洲大地我最凶!可把我自己牛逼坏了. 不扯啦不扯啦,抓紧进入今天的正题,从今天开始我会每天写一下每天工作的出现的问题,主要对这些问题出现的原因,以及 ...