1. 程式人生 > >C# 表示式樹講解(一)

C# 表示式樹講解(一)

一、前言

一直想寫一篇Dpper的定製化擴充套件的文章,但是裡面會設計到對Lambda表示式的解析,而解析Lambda表示式,就必須要知道表示式樹的相關知識點。我希望能通過對各個模組的知識點或者運用能夠多一點的講解,能夠幫助到園友瞭解得更多。雖然講解得不全面,如果能成為開啟這塊的一把鑰匙,也是蝸牛比較欣慰的。

二、表達樹理解

表示式樹以樹形資料結構表示程式碼,其中每一個節點都是一種表示式,它將我們原來可以直接由程式碼編寫的邏輯以表示式的方式儲存在樹狀的結構裡,從而可以在執行時去解析這個樹,然後執行,實現動態的編輯和執行程式碼。在.Net 裡面的Linq to SQL就是對錶達式樹的解析。

這裡先講解下表達式和表示式樹,表示式相信大家都知道,比如x+5或者5,都可以算是表示式,而表示式樹裡面的樹指的二叉樹,也就是表示式的集合,C#中的Expression類就是表示式類。對於一棵表示式樹,其葉子節點都是引數或者常數,非葉子節點都是運算子或者控制符。

2.1、表示式的建立

Lambda表示式方法:

Expression<Func<int, int,bool>> fun = (x, y) => x < y

這種方法創建出的表示式根節點型別為ExpressionType.Lambda,Type型別為返回值型別typeof(bool)

組裝法(通過 API 建立表示式樹):

ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
    Expression.Lambda<Func<int, bool>>(
        numLessThanFive,
        new ParameterExpression[] { numParam });

我們先建立了兩個引數表示式num和5,然後用LessThan組裝在一起,最終的表示式為“num<5”,expr的節點型別為LessThan,Type型別為typeof(bool)

我們先看看錶達式樹裡面的構造

首先Expression<TDelegate>的功能是將強型別Lambda表示式表示為表示式樹形式的資料結構,他的父類是LambdaExpression,比較他們程式碼可知,Lambda表示式的主體,名稱和引數全部儲存在LambdaExpression裡面。

Expression<TDelegate>與LambdaExpression程式碼截圖:

LambdaExpression裡面的Body就是我們的表示式。

C#表示式給我們提供了豐富的表示式類,進入到LambdaExpression類裡面

方法返回型別以“Expression”結尾的,基本上都是一個表示式類。

每個表示式代表的定義和建立方法,可以參照微軟官方文件https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.binaryexpression?view=netframework-4.8

下面是平常使用最多的表示式

ConstantExpression:常量表達式

ParameterExpression:引數表示式

UnaryExpression:一元運算子表示式

BinaryExpression:二元運算子表示式

TypeBinaryExpression:is運算子表示式

ConditionalExpression:條件表示式

MemberExpression:訪問欄位或屬性表示式

MethodCallExpression:呼叫成員函式表示式

Expression<TDelegate>:委託表示式

2.2、表示式的解析

表示式樹解析

通過LambdaExpression類我們可以知道,表示式樹包含:引數[Parameters],表示式樹型別[NodeType],表示式[Body],返回型別[ReturnType],Lambda表示式的委託[Compile]以及Lambda表示式名稱[name],如圖所示:

表示式解析:

所有的表示式都包含:左節點【Left】,右節點【Right】,型別【NodeType】,不同的表示式還會有其他屬性,這裡的左右節點依舊是表示式。

下圖是BinaryExpression表示式截圖

表示式樹和表示式裡面的型別NodeType是一個列舉,一共有85個型別,有興趣的朋友可以去了解下。

常用的型別如下:

ExpressionType.And:C#中類似於&

ExpressionType.AndAlso:C#中類似於&&

ExpressionType.Or:C#中類似於|

ExpressionType.OrElse:C#中類似於||

ExpressionType.Equal:C#中類似於==

ExpressionType.NotEqual:C#中類似於!=

ExpressionType.GreaterThan:C#中類似於>

ExpressionType.GreaterThanOrEqual:C#中類似於>=

ExpressionType.LessThan:C#中類似於<

ExpressionType.LessThanOrEqual:C#中類似於<=

ExpressionType.Add:C#中類似於+

ExpressionType.AddChecked:C#中類似於+

ExpressionType.Subtract:C#中類似於-

ExpressionType.SubtractChecked:C#中類似於-

ExpressionType.Divide:C#中類似於/

ExpressionType.Multiply:C#中類似於*

ExpressionType.MultiplyChecked:C#中類似於*

2.3、編譯表示式樹

在表示式建立那,我們組合建立了一個Lambda表示式,那麼應該怎麼使用它呢?在“表示式的解析”裡面,LambdaExpression類和Expression<TDelegate>類都有一個Compile的方法,學名是Lambda表示式的委託,其實就是Lambda表示式編譯函式的委託,所以我們只需要呼叫他,得到的結果就是一個函式方法。

程式碼修改如下:

ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
    Expression.Lambda<Func<int, bool>>(
        numLessThanFive,
        new ParameterExpression[] { numParam });

Console.WriteLine($"Lambda的內容:{lambda1.ToString()}");

//表示式的編譯
var func = lambda1.Compile();
Console.WriteLine($"Lambda的執行結果:{func(6)}");

執行結果

三、總結

這裡我們對錶達式做了基本的講解,相信大家對Lambda表示式有了初步的瞭解,下面我們將繼續講解對一個表示式樹的遍