服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#循环与循环控制的表达式树实现

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

2022-12-20 15:00痴者工良 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 生成的表达式树

?
1
2
3
4
5
6
7
8
9
10
11
12
13
.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:

?
1
2
3
4
5
6
7
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);
?
1
2
3
public static GotoExpression Continue(LabelTarget target, Type type);
 
public static GotoExpression Continue(LabelTarget target);

所以,要实现循环控制,必须要使用 LabelTarget,不然就无限循环了。

要理解 LabelTarget ,最好的方法是动手做。

for / while 循环

Expression.Loop 用于创建循环,包括 for 和 while,定义如下

?
1
2
3
4
5
6
  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;

无限循环

?
1
2
3
4
while (true)
{
    Console.WriteLine("无限循环");
}

那么,对应的 Loop 重载是这种

?
1
public static LoopExpression Loop(Expression body)

使用表达式树编写

?
1
2
3
4
5
6
7
8
9
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()();

最简单的循环

如果我想用表达式树做到如下最简单的循环,怎么写?

?
1
2
3
4
5
while (true)
{
    Console.WriteLine("我被执行一次就结束循环了");
    break;
}

表达式树编写

?
1
2
3
4
5
6
7
8
9
10
11
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();

生成的表达式树

?
1
2
3
4
5
6
7
8
9
.Lambda #Lambda1<System.Action>() {
    .Loop  {
        .Block() {
            .Call System.Console.WriteLine("我被执行一次就结束循环了");
            .Break #Label1 { }
        }
    }
    .LabelTarget #Label1:
}

首先要明确,Expression.Label() 里面可以为空,它是一种标记,不参与传递参数,不参与运算。有参无参,前后保持一致即可。

但是上面的循环只有一次,你可以将上面的标签改成这样试试 LabelTarget _break = Expression.Label(typeof(int));,原因后面找。

还有, Expression.Label() 变量需要一致,否则无法跳出。

试试一下代码

?
1
2
3
4
5
6
7
8
9
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() 的使用,多加实践即可。

多次循环

写一个循环十次的循环语句

?
1
2
3
4
5
6
7
8
9
for (int i = 0; i < 10; i++)
{
    if (i < 10)
    {
        Console.WriteLine(i);
    }
    else
        break;
}

或者使用 while 表示

?
1
2
3
4
5
6
7
8
9
10
11
int i = 0;
while (true)
{
    if (i < 10)
    {
        Console.WriteLine(i);
    }
    else
        break;
    i++;
}

使用表达式树编写

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();

生成的表达式树如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
.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# 循环代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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# 表达式树编写(笔者将步骤详细拆分了,所以代码比较长)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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();

生成的表达式树如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.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:
}

为了便于理解,上面的代码拆分了很多步。

来个简化版本

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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() 时,

?
1
2
Expression.Break(label);
Expression.Continu(label);

区别在于 continu 只能用 Expression.Label()。

Break 可以这样

?
1
2
3
4
LabelTarget label = Expression.Label ( typeof ( int ) );
ParameterExpression a = Expression.Variable(typeof(int), "a");
 
Expression.Break ( label , a )

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

原文链接:https://www.cnblogs.com/whuanle/p/11559795.html

延伸 · 阅读

精彩推荐
  • C#如何利用C#打印九九乘法表

    如何利用C#打印九九乘法表

    这篇文章主要给大家介绍了关于如何利用C#打印九九乘法表的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价...

    蓝色的灬|黑色的丅11252022-10-24
  • C#mvc C# JavaScript LigerUI oracle实现用户的注册、登陆验证、登陆

    mvc C# JavaScript LigerUI oracle实现用户的注册、登陆验证、登陆

    这篇文章主要介绍了mvc C# JavaScript LigerUI oracle实现用户的注册、登陆验证、登陆的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣...

    C#教程网3972021-11-18
  • C#C#给Excel添加水印实例详解

    C#给Excel添加水印实例详解

    这篇文章主要介绍了C#给Excel添加水印实例的相关资料,需要的朋友可以参考下...

    Yesi11822021-12-07
  • C#C# Linq延迟查询的执行实例代码

    C# Linq延迟查询的执行实例代码

    这篇文章主要介绍了C# Linq延迟查询执行的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    沉默♪☞小傲11512022-11-14
  • C#C#搜索文字在文件及文件夹中出现位置的方法

    C#搜索文字在文件及文件夹中出现位置的方法

    这篇文章主要介绍了C#搜索文字在文件及文件夹中出现位置的方法,涉及C#针对文件及文件夹遍历与查找的相关技巧,具有一定参考借鉴价值,需要的朋友可以参...

    北风其凉7472021-10-19
  • C#c#操作Redis的5种基本类型汇总

    c#操作Redis的5种基本类型汇总

    这篇文章主要给大家介绍了关于c#操作Redis的5种基本类型,文中通过示例代码介绍的非常详细,对大家的学习或者使用C#具有一定的参考学习价值,需要的朋...

    诗意的远方3992022-09-22
  • C#C#自定义事件之属性改变引发事件示例

    C#自定义事件之属性改变引发事件示例

    这篇文章主要为大家详细介绍了C#自定义事件之属性改变引发事件示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    cnc4512022-01-17
  • C#C# MVC模式下商品抽奖功能实现

    C# MVC模式下商品抽奖功能实现

    这篇文章主要为大家分享了C#在MVC模式下实现商品抽奖功能,思路清晰,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    熊灬孩灬子10192021-11-19