欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

表达式树练习实践:C# 五类运算符的表达式树表达

程序员文章站 2022-05-03 10:34:39
表达式树练习实践:C 运算符 [TOC] 在 C 中,算术运算符,有以下类型 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 这些运算符根据参数的多少,可以分作一元运算符、二元运算符、三元运算符。本文将围绕这些运算符,演示如何使用表达式树进行操作。 对于一元运算符和二元运算符的 ......

表达式树练习实践:c# 运算符

在 c# 中,算术运算符,有以下类型

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 其他运算符

这些运算符根据参数的多少,可以分作一元运算符、二元运算符、三元运算符。本文将围绕这些运算符,演示如何使用表达式树进行操作。

对于一元运算符和二元运算符的 expression 的子类型如下:

unaryexpression; //一元运算表达式
binaryexpression; //二元运算表达式

一,算术运算符

运算符 描述
+ 把两个操作数相加
- 从第一个操作数中减去第二个操作数
* 把两个操作数相乘
/ 分子除以分母
% 取模运算符,整除后的余数
++ 自增运算符,整数值增加 1
-- 自减运算符,整数值减少 1

+ 与 add()

正常代码

            int a;
            int b;
            a = 100;
            b = 200;
            var ab = a + b;
            console.writeline(ab);

使用表达式树构建

            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // ab = a + b
            binaryexpression ab = expression.add(a, b);

            // 打印 a + b 的值
            methodcallexpression method = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), ab);

            expression<action<int, int>> lambda = expression.lambda<action<int, int>>(method, a, b);
            lambda.compile()(100, 200);

            console.readkey();

如果想复杂一些,使用 来执行:

            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // 别忘记了赋值
            binaryexpression aa = expression.assign(a, expression.constant(100, typeof(int)));
            binaryexpression bb = expression.assign(b, expression.constant(200, typeof(int)));

            // ab = a + b
            binaryexpression ab = expression.add(a, b);

            // 打印 a + b 的值
            methodcallexpression method = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), ab);

            // 以块的形式执行代码,相当于{ }
            // 不需要纠结这里,后面会有详细说明,重点是上面
            var call = expression.block(new parameterexpression[] { a, b }, aa, bb, method);
            expression<action> lambda = expression.lambda<action>(call);
            lambda.compile()();

上面两个示例,是使用表达式树计算结果,然后还是使用表达式树打印结果。

前者依赖外界传入参数值,赋予 a、b,后者则全部使用表达式树赋值和运算。

那么,如何通过表达式树执行运算,获取执行结果呢?

            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // ab = a + b
            binaryexpression ab = expression.add(a, b);

            expression<func<int, int, int>> lambda = expression.lambda<func<int, int, int>>(ab, a, b);
            int result = lambda.compile()(100, 200);

            console.writeline(result);
            console.readkey();

这些区别在于如何编写 expression.lambda()

另外,使用 addchecked() 可以检查操作溢出。

- 与 subtract()

与加法一致,此处不再赘述,subtractchecked() 可以检查溢出。

a - b ,结果是 100 。

            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // ab = a - b
            binaryexpression ab = expression.subtract(a, b);

            expression<func<int, int, int>> lambda = expression.lambda<func<int, int, int>>(ab, a, b);
            int result = lambda.compile()(200, 100);

            console.writeline(result);

乘除、取模

乘法

            // ab = a * b
            binaryexpression ab = expression.multiply(a, b);
// ab = 20000

除法

            // ab = a / b
            binaryexpression ab = expression.divide(a, b);
// ab = 2

取模(%)

            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // ab = a % b
            binaryexpression ab = expression.modulo(a, b);

            expression<func<int, int, int>> lambda = expression.lambda<func<int, int, int>>(ab, a, b);
            int result = lambda.compile()(200, 150);
// ab = 50
            console.writeline(result);
            console.readkey();

自增自减

自增自减有两种模型,一种是 x++ 或 x--,另一种是 ++x 或 --x

他们都是属于 unaryexpression 类型。

算术运算符 表达式树 说明
x++ expression.postincrementassign() 后置
x-- expression.postdecrementassign() 后置
++x expression.preincrementassign() 前置
--x expression.predecrementassign() 前置

巧记:post 后置, pre 前置;increment 是加,decrement是减;assign与赋值有关(后面会说到);

x++x-- 的使用

            int a = 10;
            int b = 10;
            a++;
            b--;
            console.writeline(a);
            console.writeline(b);
            // int a,b;
            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // a = 10,b = 10;
            binaryexpression seta = expression.assign(a, expression.constant(10));
            binaryexpression setb = expression.assign(b, expression.constant(10));

            // a++
            unaryexpression aa = expression.postincrementassign(a);

            // b--
            unaryexpression bb = expression.postdecrementassign(b);

            //console.writeline(a);
            //console.writeline(b);
            methodcallexpression calla = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), a);
            methodcallexpression callb = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), b);

            blockexpression block = expression.block(
                new parameterexpression[] { a, b },
                seta,
                setb,
                aa,
                bb,
                calla,
                callb
                );

            expression<action> lambda = expression.lambda<action>(block);
            lambda.compile()();

            console.readkey();

如果想把参数从外面传入,设置 a,b

            // int a,b;
            parameterexpression a = expression.variable(typeof(int), "a");
            parameterexpression b = expression.variable(typeof(int), "b");

            // a++
            unaryexpression aa = expression.postincrementassign(a);

            // b--
            unaryexpression bb = expression.postdecrementassign(b);

            //console.writeline(a);
            //console.writeline(b);
            methodcallexpression calla = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), a);
            methodcallexpression callb = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), b);

            blockexpression block = expression.block(
                aa,
                bb,
                calla,
                callb
                );

            expression<action<int, int>> lambda = expression.lambda<action<int, int>>(block, a, b);
            lambda.compile()(10, 10);
            console.readkey();

生成的表达式树如下

.lambda #lambda1<system.action`2[system.int32,system.int32]>(
    system.int32 $a,
    system.int32 $b) {
    .block() {
        $a++;
        $b--;
        .call system.console.writeline($a);
        .call system.console.writeline($b)
    }
}

为了理解一下 expression.block(),可以在这里学习一下(后面会说到 block())。

            // int a,b;
            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");
            parameterexpression c = expression.variable(typeof(int), "c");

            binaryexpression seta = expression.assign(a, c);
            binaryexpression setb = expression.assign(b, c);
            // a++
            unaryexpression aa = expression.postincrementassign(a);

            // b--
            unaryexpression bb = expression.postdecrementassign(b);

            //console.writeline(a);
            //console.writeline(b);
            methodcallexpression calla = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), a);
            methodcallexpression callb = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), b);

            blockexpression block = expression.block(
                new parameterexpression[] { a, b },
                seta,
                setb,
                aa,
                bb,
                calla,
                callb
                );

            expression<action<int>> lambda = expression.lambda<action<int>>(block, c);
            lambda.compile()(10);

            console.readkey();

为什么这里要多加一个 c 呢?我们来看看生成的表达式树

.lambda #lambda1<system.action`1[system.int32]>(system.int32 $c) {
    .block(
        system.int32 $a,
        system.int32 $b) {
        $a = $c;
        $b = $c;
        $a++;
        $b--;
        .call system.console.writeline($a);
        .call system.console.writeline($b)
    }
}

观察一下下面代码生成的表达式树

            // int a,b;
            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // a++
            unaryexpression aa = expression.postincrementassign(a);

            // b--
            unaryexpression bb = expression.postdecrementassign(b);

            //console.writeline(a);
            //console.writeline(b);
            methodcallexpression calla = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), a);
            methodcallexpression callb = expression.call(null, typeof(console).getmethod("writeline", new type[] { typeof(int) }), b);

            blockexpression block = expression.block(
                new parameterexpression[] { a, b },
                aa,
                bb,
                calla,
                callb
                );

            expression<action<int, int>> lambda = expression.lambda<action<int, int>>(block, a, b);
            lambda.compile()(10, 10);
            console.readkey();
.lambda #lambda1<system.action`2[system.int32,system.int32]>(
    system.int32 $a,
    system.int32 $b) {
    .block(
        system.int32 $a,
        system.int32 $b) {
        $a++;
        $b--;
        .call system.console.writeline($a);
        .call system.console.writeline($b)
    }
}

关于前置的自增自减,按照上面示例编写即可,但是需要注意的是, ++x 和 --x ,是“先运算后增/自减”。

二,关系运算符

==、!=、>、<、>=、<=

c# 中的关系运算符如下

运算符 描述
== 检查两个操作数的值是否相等,如果相等则条件为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。

== 表示相等比较,如果是值类型和 string 类型,则比较值是否相同;如果是引用类型,则比较引用的地址是否相等。

其它的关系运算符则是仅比较值类型的大小。

实例代码

            int a = 21;
            int b = 10;
            console.write("a == b:");
            console.writeline(a == b);

            console.write("a < b :");
            console.writeline(a < b);


            console.write("a > b :");
            console.writeline(a > b);

            // 改变 a 和 b 的值 
            a = 5;
            b = 20;

            console.write("a <= b:");
            console.writeline(a <= b);


            console.write("a >= b:");
            console.writeline(b >= a);

            console.readkey();

使用表达式树实现

            // int a,b;
            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");

            // a = 21,b = 10;
            binaryexpression seta = expression.assign(a, expression.constant(21));
            binaryexpression setb = expression.assign(b, expression.constant(20));

            // console.write("a == b:");
            // console.writeline(a == b);
            methodcallexpression call1 = expression.call(null,
                typeof(console).getmethod("write", new type[] { typeof(string) }),
                expression.constant("a == b:"));
            methodcallexpression call11 = expression.call(null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.equal(a, b));

            // console.write("a < b :");
            // console.writeline(a < b);
            methodcallexpression call2 = expression.call(null,
                typeof(console).getmethod("write", new type[] { typeof(string) }),
                expression.constant("a < b :"));
            methodcallexpression call22 = expression.call(null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.lessthan(a, b));

            // console.write("a > b :");
            // console.writeline(a > b);
            methodcallexpression call3 = expression.call(null,
                typeof(console).getmethod("write", new type[] { typeof(string) }),
                expression.constant("a > b :"));
            methodcallexpression call33 = expression.call(null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.greaterthan(a, b));


            // 改变 a 和 b 的值 
            // a = 5;
            // b = 20;
            binaryexpression setaa = expression.assign(a, expression.constant(5));
            binaryexpression setbb = expression.assign(b, expression.constant(20));

            // console.write("a <= b:");
            // console.writeline(a <= b);
            methodcallexpression call4 = expression.call(null,
                typeof(console).getmethod("write", new type[] { typeof(string) }),
                expression.constant("a <= b:"));
            methodcallexpression call44 = expression.call(null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.lessthanorequal(a, b));

            // console.write("a >= b:");
            // console.writeline(b >= a);
            methodcallexpression call5 = expression.call(null,
                typeof(console).getmethod("write", new type[] { typeof(string) }),
                expression.constant("a >= b:"));
            methodcallexpression call55 = expression.call(null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.greaterthanorequal(a, b));

            blockexpression block = expression.block(new parameterexpression[] { a, b },
                seta,
                setb,
                call1,
                call11,
                call2,
                call22,
                call3,
                call33,
                setaa,
                setbb,
                call4,
                call44,
                call5,
                call55
                );

            expression<action> lambda = expression.lambda<action>(block);
            lambda.compile()();
            console.readkey();

生成的表达式树如下

.lambda #lambda1<system.action>() {
    .block(
        system.int32 $a,
        system.int32 $b) {
        $a = 21;
        $b = 20;
        .call system.console.write("a == b:");
        .call system.console.writeline($a == $b);
        .call system.console.write("a < b :");
        .call system.console.writeline($a < $b);
        .call system.console.write("a > b :");
        .call system.console.writeline($a > $b);
        $a = 5;
        $b = 20;
        .call system.console.write("a <= b:");
        .call system.console.writeline($a <= $b);
        .call system.console.write("a >= b:");
        .call system.console.writeline($a >= $b)
    }
}

三,逻辑运算符

&&、||、!

运算符 描述
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。
|| 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。

逻辑运算符的运行,结果是 true 或 false。

逻辑运算符 表达式树
&& expression.andalso()
|| expression.orelse()
expression.not()
            int a = 10;
            int b = 11;

            console.write("[a == b && a > b]:");
            console.writeline(a == b && a > b);

            console.write("[a > b || a == b]:");
            console.writeline(a > b || a == b);

            console.write("[!(a == b)]:");
            console.writeline(!(a == b));
            console.readkey();

使用表达式树编写

            //int a = 10;
            //int b = 11;
            parameterexpression a = expression.parameter(typeof(int), "a");
            parameterexpression b = expression.parameter(typeof(int), "b");
            binaryexpression seta = expression.assign(a, expression.constant(10));
            binaryexpression setb = expression.assign(b, expression.constant(11));

            //console.write("[a == b && a > b]:");
            //console.writeline(a == b && a > b);
            methodcallexpression call1 = expression.call(null, typeof(console).getmethod("write", new type[] { typeof(string) }), expression.constant("[a == b && a > b]:"));

            methodcallexpression call2 = expression.call(
                null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                 expression.andalso(expression.equal(a, b), expression.greaterthan(a, b))
                );

            //console.write("[a > b || a == b]:");
            //console.writeline(a > b || a == b);
            methodcallexpression call3 = expression.call(null, typeof(console).getmethod("write", new type[] { typeof(string) }), expression.constant("[a > b || a == b]:"));
            methodcallexpression call4 = expression.call(
                null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.orelse(expression.equal(a, b), expression.greaterthan(a, b))
                );

            //console.write("[!(a == b)]:");
            //console.writeline(!(a == b));
            methodcallexpression call5 = expression.call(null, typeof(console).getmethod("write", new type[] { typeof(string) }), expression.constant("[!(a == b)]:"));
            methodcallexpression call6 = expression.call(
                null,
                typeof(console).getmethod("writeline", new type[] { typeof(bool) }),
                expression.not(expression.equal(a, b))
                );
            blockexpression block = expression.block(
                new parameterexpression[] { a, b },
                seta,
                setb,
                call1,
                call2,
                call3,
                call4,
                call5,
                call6
                );

            expression<action> lambda = expression.lambda<action>(block);
            lambda.compile()();
            console.readkey();

生成的表达式树如下

.lambda #lambda1<system.action>() {
    .block(
        system.int32 $a,
        system.int32 $b) {
        $a = 10;
        $b = 11;
        .call system.console.write("[a == b && a > b]:");
        .call system.console.writeline($a == $b && $a > $b);
        .call system.console.write("[a > b || a == b]:");
        .call system.console.writeline($a == $b || $a > $b);
        .call system.console.write("[!(a == b)]:");
        .call system.console.writeline(!($a == $b))
    }
}

四,位运算符

&、|、^、~、<<、>>

运算符 描述 实例
& 如果同时存在于两个操作数中,二进制 and 运算符复制一位到结果中。 (a & b) 将得到 12,即为 0000 1100
| 如果存在于任一操作数中,二进制 or 运算符复制一位到结果中。 (a | b) 将得到 61,即为 0011 1101
^ 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 (a ^ b) 将得到 49,即为 0011 0001
~ 按位取反运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0,包括符号位。 (~a ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<< 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 a << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 a >> 2 将得到 15,即为 0000 1111

限于篇幅,就写示例了。

位运算符 表达式树
& expression.add(expression left, expression right)
| expression.or(expression left, expression right)
^ expression.exclusiveor(expression expression)
~ expression.onescomplement( expression expression)
<< expression.leftshift(expression left, expression right)
>> expression.rightshift(expression left, expression right)

五,赋值运算符

运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 c = a + b 将把 a + b 的值赋给 c
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 c += a 相当于 c = c + a
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 c -= a 相当于 c = c - a
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 c = a 相当于 c = c a
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 c /= a 相当于 c = c / a
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 c %= a 相当于 c = c % a
<<= 左移且赋值运算符 c <<= 2 等同于 c = c << 2
>>= 右移且赋值运算符 c >>= 2 等同于 c = c >> 2
&= 按位与且赋值运算符 c &= 2 等同于 c = c & 2
^= 按位异或且赋值运算符 c ^= 2 等同于 c = c ^ 2
|= 按位或且赋值运算符 c |= 2 等同于 c = c | 2

限于篇幅,请自行领略... ...

运算符 表达式树
= expression.assign
+= expression.addassign
-= expression.subtractassign
*= expression.multiplyassign
/= expression.divideassign
%= expression.moduloassign
<<= expression.leftshiftassign
>>= expression.rightshiftassign
&= expression.andassign
^= expression.exclusiveorassign
|= expression.orassign

^= ,注意有两种意思一种是位运算符的异或(exclusiveorassign),一种是算术运算符的幂运算(powerassign)

六,其他运算符

运算符 描述 实例
sizeof() 返回数据类型的大小。 sizeof(int),将返回 4.
typeof() 返回 class 的类型。 typeof(streamreader);
& 返回变量的地址。 &a; 将得到变量的实际地址。
* 变量的指针。 *a; 将指向一个变量。
? : 条件表达式 如果条件为真 ? 则为 x : 否则为 y
is 判断对象是否为某一类型。 if( ford is car) // 检查 ford 是否是 car 类的一个对象。
as 强制转换,即使转换失败也不会抛出异常。 object obj = new stringreader("hello"); stringreader r = obj as stringreader;

表达式树里面我没有找到这些运算符的如何编写,如果你找到了,欢迎告诉我。。。