6. 循环控制
[TOC]
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 生成的表达式树
1
.Lambda #Lambda1<System.Func`1[System.Int32]>() {
2
.Block(System.Int32 $x) {
3
$x = 0;
4
.Loop {
5
.If ($x < 10) {
6
$x++
7
} .Else {
8
.Break #Label1 { $x }
9
}
10
}
11
.LabelTarget #Label1:
12
}
13
}
Copied!
要实现循环控制,有 break,contauine 两种 Expression:
1
public static GotoExpression Break(LabelTarget target, Type type);
2
3
public static GotoExpression Break(LabelTarget target, Expression value);
4
5
public static GotoExpression Break(LabelTarget target);
6
7
public static GotoExpression Break(LabelTarget target, Expression value, Type type);
Copied!
1
public static GotoExpression Continue(LabelTarget target, Type type);
2
3
public static GotoExpression Continue(LabelTarget target);
Copied!
所以,要实现循环控制,必须要使用 LabelTarget,不然就无限循环了。
要理解 LabelTarget ,最好的方法是动手做。

for / while 循环

Expression.Loop 用于创建循环,包括 for 和 while,定义如下
1
public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue);
2
3
System.Linq.Expressions.LoopExpression.
4
public static LoopExpression Loop(Expression body);
5
6
public static LoopExpression Loop(Expression body, LabelTarget @break);
Copied!
表达式树里面的循环,只有 Loop,无 for / while 的区别。
那么,我们来一步步理解 Loop 循环和 LabelTarget;

无限循环

1
while (true)
2
{
3
Console.WriteLine("无限循环");
4
}
Copied!
那么,对应的 Loop 重载是这种
1
public static LoopExpression Loop(Expression body)
Copied!
使用表达式树编写
1
BlockExpression _block = Expression.Block(
2
new ParameterExpression[] { },
3
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),Expression.Constant("无限循环") )
4
);
5
6
LoopExpression _loop = Expression.Loop(_block);
7
8
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
9
lambda.Compile()();
Copied!

最简单的循环

如果我想用表达式树做到如下最简单的循环,怎么写?
1
while (true)
2
{
3
Console.WriteLine("我被执行一次就结束循环了");
4
break;
5
}
Copied!
表达式树编写
1
LabelTarget _break = Expression.Label();
2
3
BlockExpression _block = Expression.Block(
4
new ParameterExpression[] { },
5
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被执行一次就结束循环了")), Expression.Break(_break));
6
LoopExpression _loop = Expression.Loop(_block, _break);
7
8
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
9
lambda.Compile()();
10
11
Console.ReadKey();
Copied!
生成的表达式树
1
.Lambda #Lambda1<System.Action>() {
2
.Loop {
3
.Block() {
4
.Call System.Console.WriteLine("我被执行一次就结束循环了");
5
.Break #Label1 { }
6
}
7
}
8
.LabelTarget #Label1:
9
}
Copied!
首先要明确,Expression.Label() 里面可以为空,它是一种标记,不参与传递参数,不参与运算。有参无参,前后保持一致即可。
但是上面的循环只有一次,你可以将上面的标签改成这样试试 LabelTarget _break = Expression.Label(typeof(int));,原因后面找。
还有, Expression.Label() 变量需要一致,否则无法跳出。
试试一下代码
1
BlockExpression _block = Expression.Block(
2
new ParameterExpression[] { },
3
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被执行一次就结束循环了")), Expression.Break(Expression.Label()));
4
LoopExpression _loop = Expression.Loop(_block, Expression.Label());
5
6
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
7
lambda.Compile()();
8
9
Console.ReadKey();
Copied!
里面用到了 Expression.Block(),Block() 是块,即{}。
如果 Block() 是在最外层,那么相当于是函数;如果是内嵌,相当于{};
但不是真的这样。。。表达式树里面不是完全按照 C# 的语法来还原操作的。
对于 Block() 的使用,多加实践即可。

多次循环

写一个循环十次的循环语句
1
for (int i = 0; i < 10; i++)
2
{
3
if (i < 10)
4
{
5
Console.WriteLine(i);
6
}
7
else
8
break;
9
}
Copied!
或者使用 while 表示
1
int i = 0;
2
while (true)
3
{
4
if (i < 10)
5
{
6
Console.WriteLine(i);
7
}
8
else
9
break;
10
i++;
11
}
Copied!
使用表达式树编写
1
LabelTarget _break = Expression.Label(typeof(int));
2
ParameterExpression a = Expression.Variable(typeof(int), "a");
3
4
BlockExpression _block = Expression.Block(new ParameterExpression[] { },
5
Expression.IfThenElse
6
(
7
Expression.LessThan(a, Expression.Constant(10)),
8
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
9
Expression.Break(_break, a)
10
),
11
Expression.PostIncrementAssign(a) // a++
12
);
13
14
15
LoopExpression _loop = Expression.Loop(_block, _break);
16
17
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(_loop, a);
18
lambda.Compile()(0);
19
Console.ReadKey();
Copied!
生成的表达式树如下
1
.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
2
.Loop {
3
.Block() {
4
.If ($a < 10) {
5
.Call System.Console.WriteLine($a)
6
} .Else {
7
.Break #Label1 { $a }
8
};
9
$a++
10
}
11
}
12
.LabelTarget #Label1:
13
}
Copied!
试试将 Expression.Break(_break, a) 改成 Expression.Break(_break)。看看报什么错。。。
解决方法是,上面的标记也改成 LabelTarget _break = Expression.Label();
就跟你写代码写注释一样,里面的东西是为了让别人看代码是容易理解。
有些同学纠结于 Expression.Label(有参或无参);Expression.Break(_break, a)Expression.Break(_break),只要看看最终生成的表达式树就清楚了。

break 和 continue 一起

C# 循环代码如下
1
int i = 0;
2
while (true)
3
{
4
if (i < 10)
5
{
6
if (i % 2 == 0)
7
{
8
Console.Write("i是偶数:");
9
Console.WriteLine(i);
10
i++;
11
continue;
12
}
13
Console.WriteLine("其他任务 --");
14
Console.WriteLine("其他任务 --");
15
}
16
else break;
17
i++;
18
}
Copied!
使用 C# 表达式树编写(笔者将步骤详细拆分了,所以代码比较长)
1
ParameterExpression a = Expression.Variable(typeof(int), "a");
2
3
LabelTarget _break = Expression.Label();
4
LabelTarget _continue = Expression.Label();
5
6
// if (i % 2 == 0)
7
// {
8
// Console.Write("i是偶数:");
9
// Console.WriteLine(i);
10
// i++;
11
// continue;
12
// }
13
ConditionalExpression _if = Expression.IfThen(
14
Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
15
Expression.Block(
16
new ParameterExpression[] { },
17
Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶数:")),
18
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
19
Expression.PostIncrementAssign(a),
20
Expression.Continue(_continue)
21
)
22
);
23
24
// if (i % 2 == 0)
25
// {
26
// Console.Write("i是偶数:");
27
// Console.WriteLine(i);
28
// i++;
29
// continue;
30
// }
31
// Console.WriteLine("其他任务 --");
32
// Console.WriteLine("其他任务 --");
33
BlockExpression block1 = Expression.Block(
34
new ParameterExpression[] { },
35
_if,
36
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --")),
37
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --"))
38
);
39
40
// if (i < 10)
41
// {
42
// if (i % 2 == 0)
43
// {
44
// Console.Write("i是偶数:");
45
// Console.WriteLine(i);
46
// i++;
47
// continue;
48
// }
49
// Console.WriteLine("其他任务 --");
50
// Console.WriteLine("其他任务 --");
51
// }
52
// else break;
53
ConditionalExpression if_else = Expression.IfThenElse(
54
Expression.LessThan(a, Expression.Constant(10)),
55
block1,
56
Expression.Break(_break)
57
);
58
59
60
// if (i < 10)
61
// {
62
// if (i % 2 == 0)
63
// {
64
// Console.Write("i是偶数:");
65
// Console.WriteLine(i);
66
// i++;
67
// continue;
68
// }
69
// Console.WriteLine("其他任务 --");
70
// Console.WriteLine("其他任务 --");
71
// }
72
// else break;
73
// i++ ;
74
75
BlockExpression block2 = Expression.Block(
76
new ParameterExpression[] { },
77
if_else,
78
Expression.PostIncrementAssign(a)
79
);
80
// while(true)
81
LoopExpression loop = Expression.Loop(block2, _break, _continue);
82
83
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
84
lambda.Compile()(0);
85
Console.ReadKey();
Copied!
生成的表达式树如下
1
.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
2
.Loop .LabelTarget #Label1: {
3
.Block() {
4
.If ($a < 10) {
5
.Block() {
6
.If (
7
$a % 2 == 0
8
) {
9
.Block() {
10
.Call System.Console.Write("i是偶数:");
11
.Call System.Console.WriteLine($a);
12
$a++;
13
.Continue #Label1 { }
14
}
15
} .Else {
16
.Default(System.Void)
17
};
18
.Call System.Console.WriteLine("其他任务 --");
19
.Call System.Console.WriteLine("其他任务 --")
20
}
21
} .Else {
22
.Break #Label2 { }
23
};
24
$a++
25
}
26
}
27
.LabelTarget #Label2:
28
}
Copied!
为了便于理解,上面的代码拆分了很多步。
来个简化版本
1
ParameterExpression a = Expression.Variable(typeof(int), "a");
2
3
LabelTarget _break = Expression.Label();
4
LabelTarget _continue = Expression.Label();
5
6
LoopExpression loop = Expression.Loop(
7
Expression.Block(
8
new ParameterExpression[] { },
9
Expression.IfThenElse(
10
Expression.LessThan(a, Expression.Constant(10)),
11
Expression.Block(
12
new ParameterExpression[] { },
13
Expression.IfThen(
14
Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
15
Expression.Block(
16
new ParameterExpression[] { },
17
Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶数:")),
18
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
19
Expression.PostIncrementAssign(a),
20
Expression.Continue(_continue)
21
)
22
),
23
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --")),
24
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任务 --"))
25
),
26
Expression.Break(_break)
27
),
28
Expression.PostIncrementAssign(a)
29
),
30
_break,
31
_continue
32
);
33
34
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
35
lambda.Compile()(0);
36
Console.ReadKey();
Copied!
需要注意的是,Expression.Break Expression.Continue 有所区别。
当标签实例化都是 Expression.Label() 时,
1
Expression.Break(label);
2
Expression.Continu(label);
Copied!
区别在于 continu 只能用 Expression.Label()。
Break 可以这样
1
LabelTarget label = Expression.Label ( typeof ( int ) );
2
ParameterExpression a = Expression.Variable(typeof(int), "a");
3
4
Expression.Break ( label , a )
Copied!