当前位置: 移动技术网 > IT编程>开发语言>c# > 表达式树练习实践:C# 五类运算符的表达式树表达

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

2019年09月19日  | 移动技术网IT编程  | 我要评论
表达式树练习实践: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;

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

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

相关文章:

验证码:
移动技术网