当前位置: 移动技术网 > IT编程>开发语言>c# > 表达式树练习实践:C# 循环与循环控制

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

2019年09月21日  | 移动技术网IT编程  | 我要评论
表达式树练习实践:C 循环 [TOC] C 提供了以下几种循环类型。 | 循环类型 | 描述 | | : | : | | while 循环 | 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 | | for/foreach 循环 | 多次执行一个语句序列,简化管理循环变量的代码 ...

表达式树练习实践:c# 循环

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 生成的表达式树

.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 ) 

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网