c# 表達式樹(一)


前言

打算整理c# 代碼簡化史系列,所以相關的整理一下,簡單的引出一下概念。

什么是表達式樹呢?

表達式樹以樹形數據結構表示代碼,其中每一個節點都是一種表達式,比如方法調用和 x < y 這樣的二元運算等。

這個是什么意思呢?用結構表示代碼? 用靜態的表示動態的,一般來說是某種約定。

比如計算機中的強弱電路,可能這樣不好理解。舉一個盒子的例子:

假設我要計算加法,那么如果表示加法呢?我用一個盒型結構,把第一個數放在第一個位置,把第二個數放在第二個位置,然后第三個位置我傳入方法,表示第一個和第二個會執行第三個位置的方法,在這里呢,還是結構,因為並沒有去運行,只是說組合了這樣一種結構。

現在呢,假設按照某種約定組合成一種結構,那么這種就稱為表達式,就是用來表示某種情況的嘛。然后呢,現在這種表達式是樹,那么就叫表達式樹了。

這里介紹一下表達式,來增強一波:

然后再來透析一波:

正文

用一個例子來表示正則表達吧,例子是官網的,但是官網解釋的比較含糊,所以再來解釋一波吧。

官網用的一個例子是:Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).orderby(company=>company)

那么來看一下吧:

string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
	   "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
	   "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
	   "Blue Yonder Airlines", "Trey Research", "The Phone Company",
	   "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };

// The IQueryable data to query.  
IQueryable<String> queryableData = companies.AsQueryable<string>();

有一個數組,然后轉換成IQueryable 格式,這么做的目的其實就是因為queryable 實現了一些expression的屬性。

好吧,暫時就不解釋這幾個參數的作用,后面看下去自然就明白了。

接着放代碼:

ParameterExpression pe = Expression.Parameter(typeof(string), "company");

這個意思就是說創建了一個屬性是company的變量,相當於我們以前的xy,名字隨便取。

Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Expression right = Expression.Constant("coho winery");
Expression e1 = Expression.Equal(left, right);

因為其實樹結構,那么這里的參數left就是左子樹,right 就是右子樹。

left 呢,這個call 就是說pe(也就是company變量)將會執行一個方法,ToLower,對應的就是company.ToLower()。

然后右邊就是一個固定的參數coho winery,現在的表達式就是company.ToLower()=='coho winery',返回的是一個bool類型。

left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16, typeof(int));
Expression e2 = Expression.GreaterThan(left, right);

接下來就是就是獲取compay的屬性Length,然后和int 類型相比,就是conpany.length>16
Expression predicateBody = Expression.OrElse(e1, e2);
那么就是e1和e2相連,中間用的是or,company.ToLower() == "coho winery" || company.Length > 16 好的現在表達式完了,那么如何和數據聯系在一起呢?

// Create an expression tree that represents the expression  
// 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'  
MethodCallExpression whereCallExpression = Expression.Call(
	typeof(Queryable),
	"Where",
	new Type[] { queryableData.ElementType },
	queryableData.Expression,
	Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
// ***** End Where *****  

// ***** OrderBy(company => company) *****  
// Create an expression tree that represents the expression  
// 'whereCallExpression.OrderBy(company => company)'  
MethodCallExpression orderByCallExpression = Expression.Call(
	typeof(Queryable),
	"OrderBy",
	new Type[] { queryableData.ElementType, queryableData.ElementType },
	whereCallExpression,
	Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****  

// Create an executable query from the expression tree.  
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

whereCallExpression 和 orderByCallExpression 表示要執行的操作,用whereCallExpression 舉例目標類型是Queryable,調用where,然后表達式是predicateBody,參數是pe。

orderByCallExpression 類推。

最后一步就是傳遞表達:IQueryable results = queryableData.Provider.CreateQuery (orderByCallExpression);

里面的實現是非常復雜的,我自己也沒有去看,因為覺得沒有必要,這種東西就是一個工具,誰要是這樣寫,那可正是思維邏輯不是一般的強,一般來說和匯編差不多。

需要明白的就是它不會立即去執行,而是就是一個表達式和其緊密連接。有興趣可以去了解iqueryable的實現,復雜的一批。

那么不管其多么復雜,就是本質上就是制定一套規則,我們按照它這個規則然后給我們填充,那么就會對應相應的結果給我們,我們可能設計不出這么好的表達式,但是有時候我們也會去制作相應的規則,比如說某種格式等,但你不要去想想它的代碼多優雅,因為其穩定性很高,不需要追求優雅。

那么我們為了延后實現,我們就要去這樣做嗎?如果這樣做的話,我想很多人會設計出另外一套,沒有這么繁瑣,可能就是幾個參數,然后一個委托組合成一顆樹,雖然很大的局限性,但是寫這樣的代碼真的痛苦。

這個時候人們就想有沒有什么能中間轉換一下的呢?比如說我寫一串字符,然后我就自動按照某種規則去解析不就可以了,但是這種有一個很不好的地方在於,字符串是弱類型調試相當麻煩,這時候就瞄准好了lambda了。

若 lambda 表達式被分配給 Expression<TDelegate> 類型的變量,則編譯器可以發射代碼以創建表示該 lambda 表達式的表達式樹。

C# 編譯器只能從表達式 Lambda(或單行 Lambda)生成表達式樹。 它無法解析語句 lambda (或多行 lambda)。

舉個例子:

Expression<Func<int, bool>> lambda = num => num < 5;  

就可以使用lambda表達式進行一個expression的轉換。

從Expression到Expression 之間呢,還有一層,他們的繼承關系是

 Object
 Expression
 LambdaExpression
 Expression<TDelegate>

很多時候lambda 表達式轉換的表達式就可以為我們解決大部分問題,但是不要覺得這是Expression的全部,因為轉換的只有一行,然后expression還有很多是無法用lambda來表示的。

未完待續。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM