表達式樹練習實踐:入門基礎

  • 2019 年 10 月 3 日
  • 筆記

表達式樹練習實踐:入門基礎

img

什麼是表達式樹

來自微軟官方文檔的定義:

表達式樹以樹形數據結構表示代碼。

它能幹什麼呢?

你可以對表達式樹中的代碼進行編輯和運算。 這樣能夠動態修改可執行代碼、在不同數據庫中執行 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,所以有很多屬性方法也可以用。

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

初學者不必糾結於這些,了解一下本文內容,記一下概要信息即可。