C#表達(dá)式樹基礎(chǔ)教程
什么是表達(dá)式樹
來自微軟官方文檔的定義:
表達(dá)式樹以樹形數(shù)據(jù)結(jié)構(gòu)表示代碼。
它能干什么呢?
你可以對表達(dá)式樹中的代碼進(jìn)行編輯和運(yùn)算。 這樣能夠動(dòng)態(tài)修改可執(zhí)行代碼、在不同數(shù)據(jù)庫中執(zhí)行 LINQ 查詢以及創(chuàng)建動(dòng)態(tài)查詢。
好不好玩?
表達(dá)式樹還能用于動(dòng)態(tài)語言運(yùn)行時(shí) (DLR) 以提供動(dòng)態(tài)語言和 .NET Framework 之間的互操作性,同時(shí)保證編譯器編寫員能夠發(fā)射表達(dá)式樹而非 Microsoft 中間語言 (MSIL)。
哪里有應(yīng)用?
ORM框架、工作流框架等,使用到 Lambda 的代碼。。。動(dòng)態(tài)執(zhí)行代碼、動(dòng)態(tài)組裝代碼等。
創(chuàng)建表達(dá)式樹
創(chuàng)建表達(dá)式樹有兩種方式:通過 lambda 表達(dá)式、通過 API。
創(chuàng)建表達(dá)式樹的意思是,在此之前已經(jīng)編寫好每個(gè)結(jié)點(diǎn),最后使用代碼將所有結(jié)點(diǎn)組合起來,生成表達(dá)式樹。
示例(通過API創(chuàng)建表達(dá)式樹)
ParameterExpression a = Expression.Parameter(typeof(int), "i"); ParameterExpression b = Expression.Parameter(typeof(int), "j"); Expression r1 = Expression.Multiply(a, b); //乘法運(yùn)行 ParameterExpression c = Expression.Parameter(typeof(int), "x"); ParameterExpression d = Expression.Parameter(typeof(int), "y"); Expression r2 = Expression.Multiply(c, d); //乘法運(yùn)行 Expression result = Expression.Add(r1, r2); //相加 //以上代碼產(chǎn)生結(jié)點(diǎn) //生成表達(dá)式 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("表達(dá)式" + func); Console.WriteLine(com(12, 12, 13, 13)); Console.ReadKey();
上面關(guān)于表達(dá)式樹的代碼很多,以下這一步叫生成/創(chuàng)建表達(dá)式樹。
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
以下這句叫執(zhí)行表達(dá)式樹
var com = func.Compile();
其它代碼是用于生成表達(dá)式樹結(jié)點(diǎn)/邏輯。
回歸正題,創(chuàng)建表達(dá)式樹的兩種方法。
lambda 創(chuàng)建表達(dá)式樹
上面的表達(dá)式樹示例,是用于生成
( i * j ) + ( x * y )
但是就這么簡單的操作,要寫這么長,實(shí)在不合理。
而通過 lambda ,可以這樣寫
Expression<Func<int, int, int, int, int>> func = (i, j, x, y) => (i * j) + (x * y);
如果使用 lambda 生成表達(dá)式樹, lambda 只能使用單行語句,不能使用 if、for等語句。
具體關(guān)于 Lambda 的表達(dá)式樹,后面其它文章有說明。
通過 API 創(chuàng)建表達(dá)式樹
就是這樣
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
兩種方式左邊的都是一樣的,區(qū)別在于等號(hào)右邊。
Expression< TDelegate >
上面示例的最終結(jié)果都是生成
Expression<Func<int, int, int, int, int>> func
func 是表達(dá)式樹變量。
我們可以了解以下表達(dá)式樹具有的方法和屬性。
用于生成表達(dá)式樹結(jié)點(diǎn)的,是 Expression 類型。
那么,創(chuàng)建的表達(dá)式樹 func ,是 Expression<TDelegate>
類型。
定義如下
public sealed class Expression<TDelegate> : LambdaExpression
具有方法如下
方法 | 說明 |
---|---|
Compile() | 將表達(dá)式樹描述的 lambda 表達(dá)式編譯為可執(zhí)行代碼,并生成表示 lambda 表達(dá)式的委托。 |
Compile(Boolean) | 將表達(dá)式樹描述的 Lambda 表達(dá)式編譯為已解釋或已編譯的代碼,并生成表示該 Lambda 表達(dá)式的委托。 |
Compile(DebugInfoGenerator) | 將 lambda 編譯到方法定義中。 (Inherited from LambdaExpression) |
Update(Expression, IEnumerable) | 創(chuàng)建一個(gè)與此表達(dá)式類似的新表達(dá)式,但使用所提供的子級(jí)。 如果所有子級(jí)都相同,則將返回此表達(dá)式。 |
Accept(ExpressionVisitor) | 調(diào)度到此節(jié)點(diǎn)類型的特定 Visit 方法。 例如,MethodCallExpression調(diào)用 VisitMethodCall。 |
由于 Expression<TDelegate>
繼承了 LambdaExpression
,所以有很多屬性方法也可以用。
Body | 獲取 lambda 表達(dá)式的主體。 |
---|---|
CanReduce | 指示可將節(jié)點(diǎn)簡化為更簡單的節(jié)點(diǎn)。 如果返回 true,則可以調(diào)用 Reduce() 以生成簡化形式。 |
Name | 獲取 lambda 表達(dá)式的名稱。 |
NodeType | 返回此 Expression 的節(jié)點(diǎn)類型。 |
Parameters | 獲取 lambda 表達(dá)式的參數(shù)。 |
ReturnType | 獲取 lambda 表達(dá)式的返回類型。 |
TailCall | 獲取一個(gè)值,該值指示是否將通過尾調(diào)用優(yōu)化來編譯 lambda 表達(dá)式。 |
Type | 獲取此 Expression 表示的表達(dá)式的靜態(tài)類型。 |
好了,以上權(quán)當(dāng)小筆記,備忘,目前先用不上,后面慢慢來使用。
解析/執(zhí)行表達(dá)式樹
創(chuàng)建表達(dá)式樹后,就要執(zhí)行表達(dá)式樹。
在此之前,你需要了解 委托 Delegate,F(xiàn)unc,Action,以及他們中間的關(guān)系。
執(zhí)行表達(dá)式樹是這樣子的
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 只是一個(gè)表達(dá)式樹,我們把表達(dá)式樹構(gòu)建好后,“要將表達(dá)式樹轉(zhuǎn)為代碼”,使用
.Compile()
方法,可以將表達(dá)式樹生成一個(gè) 委托(例如上面的 com)。
為了簡潔上面使用了 var,實(shí)際上是這樣的
Func<int,int,int,int,int> com = func.Compile();
四個(gè)參數(shù),一個(gè)返回值。
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 里面調(diào)試和查看表達(dá)式樹,可以看這里
到此這篇關(guān)于C#表達(dá)式樹基礎(chǔ)教程的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Unity3D實(shí)現(xiàn)模型隨機(jī)切割
這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)模型隨機(jī)切割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03asp.net中調(diào)用oracle存儲(chǔ)過程的方法
存儲(chǔ)過程是在大型數(shù)據(jù)庫系統(tǒng)中,一組為了完成特定功能的SQL 語句集,存儲(chǔ)在數(shù)據(jù)庫中經(jīng)過第一次編譯后再次調(diào)用不需要再次編譯,用戶通過指定存儲(chǔ)過程的名字并給出參數(shù)來執(zhí)行它,下面給大家介紹下asp.net中調(diào)用oracle存儲(chǔ)過程的方法,需要的朋友可以參考下2015-08-08C#與C++之間類型的對應(yīng)知識(shí)點(diǎn)總結(jié)
這篇文章主要介紹了C#與C++之間類型的對應(yīng)知識(shí)點(diǎn)總結(jié),對此有需要的朋友們可以參考下。2019-08-08