表示式樹練習實踐:入門基礎
目錄
- 表示式樹練習實踐:入門基礎
- 什麼是表示式樹
- 建立表示式樹
- lambda 建立表示式樹
- 通過 API 建立表示式樹
- Expression< TDelegate >
- 解析/執行表示式樹
表示式樹練習實踐:入門基礎
什麼是表示式樹
來自微軟官方文件的定義:
表示式樹以樹形資料結構表示程式碼。
它能幹什麼呢?
你可以對錶達式樹中的程式碼進行編輯和運算。 這樣能夠動態修改可執行程式碼、在不同資料庫中執行 LINQ 查詢以及建立動態查詢。
好不好玩?
表示式樹還能用於動態語言執行時 (DLR) 以提供動態語言和 .NET Framework 之間的互操作性,同時保證編譯器編寫員能夠發射表示式樹而非 Microsoft 中間語言 (MSIL)。
哪裡有應用?
ORM框架、工作流框架等,使用到 Lambda 的程式碼。。。動態執行程式碼、動態組裝程式碼等。
建立表示式樹
建立表示式樹有兩種方式:通過 lambda 表示式、通過 API。
建立表示式樹的意思是,在此之前已經編寫好每個結點,最後使用程式碼將所有結點組合起來,生成表示式樹。
示例(通過API建立表示式樹)
``` ParameterExpression a = Expression.Parameter(typeof(int), "i"); ParameterExpression b = Expression.Parameter(typeof(int), "j"); Expression r1 = Expression.Multiply(a, b); //乘法執行 ParameterExpression c = Expression.Parameter(typeof(int), "x"); ParameterExpression d = Expression.Parameter(typeof(int), "y"); Expression r2 = Expression.Multiply(c, d); //乘法執行 Expression result = Expression.Add(r1, r2); //相加 //以上程式碼產生結點 //生成表示式 Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d); var com = func.Compile(); Console.WriteLine("表示式" + func); Console.WriteLine(com(12, 12, 13, 13)); Console.ReadKey();
上面關於表示式樹的程式碼很多,以下這一步叫生成/建立表示式樹。
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
以下這句叫執行表示式樹
var com = func.Compile();
其它程式碼是用於生成表示式樹結點/邏輯。
迴歸正題,建立表示式樹的兩種方法。
lambda 建立表示式樹
上面的表示式樹示例,是用於生成
( i * j ) + ( x * y )
但是就這麼簡單的操作,要寫這麼長,實在不合理。
而通過 lambda ,可以這樣寫
Expression<Func<int, int, int, int, int>> func = (i, j, x, y) => (i * j) + (x * y);
如果使用 lambda 生成表示式樹, lambda 只能使用單行語句,不能使用 if、for等語句。
具體關於 Lambda 的表示式樹,後面其它文章有說明。
通過 API 建立表示式樹
就是這樣
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
兩種方式左邊的都是一樣的,區別在於等號右邊。
Expression< TDelegate >
上面示例的最終結果都是生成
Expression<Func<int, int, int, int, int>> func
func 是表示式樹變數。
我們可以瞭解以下表達式樹具有的方法和屬性。
用於生成表示式樹結點的,是 Expression 型別。
那麼,建立的表示式樹 func ,是 Expression<TDelegate>
型別。
定義如下
public sealed class Expression<TDelegate> : LambdaExpression
具有方法如下
方法 | 說明 |
---|---|
Compile() | 將表示式樹描述的 lambda 表示式編譯為可執行程式碼,並生成表示 lambda 表示式的委託。 |
Compile(Boolean) | 將表示式樹描述的 Lambda 表示式編譯為已解釋或已編譯的程式碼,並生成表示該 Lambda 表示式的委託。 |
Compile(DebugInfoGenerator) | 將 lambda 編譯到方法定義中。 (Inherited from LambdaExpression) |
Update(Expression, IEnumerable |
建立一個與此表示式類似的新表示式,但使用所提供的子級。 如果所有子級都相同,則將返回此表示式。 |
Accept(ExpressionVisitor) | 排程到此節點型別的特定 Visit 方法。 例如,MethodCallExpression呼叫 VisitMethodCall。 |
由於 Expression<TDelegate>
繼承了 LambdaExpression
,所以有很多屬性方法也可以用。
Body | 獲取 lambda 表示式的主體。 |
---|---|
CanReduce | 指示可將節點簡化為更簡單的節點。 如果返回 true,則可以呼叫 Reduce() 以生成簡化形式。 |
Name | 獲取 lambda 表示式的名稱。 |
NodeType | 返回此 Expression 的節點型別。 |
Parameters | 獲取 lambda 表示式的引數。 |
ReturnType | 獲取 lambda 表示式的返回型別。 |
TailCall | 獲取一個值,該值指示是否將通過尾呼叫優化來編譯 lambda 表示式。 |
Type | 獲取此 Expression 表示的表示式的靜態型別。 |
好了,以上權當小筆記,備忘,目前先用不上,後面慢慢來使用。
解析/執行表示式樹
建立表示式樹後,就要執行表示式樹。
在此之前,你需要了解 委託 Delegate,Func,Action,以及他們中間的關係。
執行表示式樹是這樣子的
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
var com = func.Compile();
var runRasult = com(12, 12, 13, 13);
func 只是一個表示式樹,我們把表示式樹構建好後,“要將表示式樹轉為程式碼”,使用
.Compile()
方法,可以將表示式樹生成一個 委託(例如上面的 com)。
為了簡潔上面使用了 var,實際上是這樣的
Func<int,int,int,int,int> com = func.Compile();
四個引數,一個返回值。
var runRasult = com(12, 12, 13, 13);
C#裡有語法糖,對委託可以這樣寫
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
int runRasult = func.Compile()(12, 12, 13, 13);
以後後面都是這樣寫了,能夠縮成一行的程式碼,就沒必要寫出兩行。
在 Vs 裡面除錯和查看錶達式樹,可以看這裡
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/expression-trees/debugging-expression-trees-in-visual-studio
初學者不必糾結於這些,瞭解一下本文內容,記一下概要資訊即