表达式树练习实践: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; |
表达式树里面我没有找到这些运算符的如何编写,如果你找到了,欢迎告诉我。。。