表示式樹練習實踐:C# 五類運算子的表示式樹表達
目錄
- 表示式樹練習實踐:C# 運算子
- 一,算術運算子
- + 與 Add()
- - 與 Subtract()
- 乘除、取模
- 自增自減
- 二,關係運算符
- ==、!=、>、<、>=、<=
- 三,邏輯運算子
- &&、||、!
- 四,位運算子
- &、|、^、~、<<、>>
- 五,賦值運算子
- 六,其他運算子
- 一,算術運算子
表示式樹練習實踐: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; |
表示式樹裡面我沒有找到這些運算子的如何編寫,如果你找到了,歡迎告訴我。