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

网友投稿 261 2022-06-21


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() {

.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 lambda = Expression.Lambda(_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 lambda = Expression.Lambda(_loop);

lambda.Compile()();

Console.ReadKey();

生成的表达式树

.Lambda #Lambda1() {

.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 lambda = Expression.Lambda(_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> lambda = Expression.Lambda>(_loop, a);

lambda.Compile()(0);

Console.ReadKey();

生成的表达式树如下

.Lambda #Lambda1(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> lambda = Expression.Lambda>(loop, a);

lambda.Compile()(0);

Console.ReadKey();

生成的表达式树如下

.Lambda #Lambda1(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> lambda = Expression.Lambda>(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 )


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:C#程序员在老项目中用到VB遇到的一次坑(c反应蛋白高说明什么)
下一篇:Threads(异步和多线程)(异步与多线程)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~