在上一篇中,我們認識了什么是表達式樹、什么是委托,以及它們的關系。可能是我功力不好,貌似大家都不怎么關注,沒有講解出不同角度的問題。
學習一種新技術,是枯燥的過程,只有在你掌握后並能運用時才能從它身上得到樂趣。
做程序開發是一群很奇怪的人群,我們居然可以通過密密麻麻的英文字符加上標點符號從中找到樂趣,確實很奇怪。
考慮到大家接觸新技術需要一個過程:
其實對於很多人來說已經不是新技術了,不過您會耐心看本篇后續的文章,說明您可能對這一項技術運用的並不是很熟練
所以我不打算一上來,就放一大堆代碼,然后解釋這是什么,那是什么,因為會接觸很多新的關鍵詞,這樣大家學習起來會也會很痛苦
本系列后面的所有篇幅都只在每篇中提一個新概念,這樣大家學習起來可以減少學習的范圍。
然后也讓大家徹底搞懂每種類型的作用,同時我會用Lambda方式、動態構造、以及表達式樹的結構三種方式來共同研究每篇課題的新類型。
LambdaExpression是繼承自Expression的。LambdaExpression的具體表現是:Expression<Func<>>或者Expression<Action<>>
這段不明白沒關系,看下面示例就知道了
首先,我們先從MSDN上看它的注釋說明:
描述一個 lambda 表達式。 這將捕獲與 .NET 方法體類似的代碼塊。
看MSDN注釋,我們還是沒搞懂它是什么意思,通俗的講:一段包含有運算表達式的代碼,就是LambdaExpression。
好吧,我說了吧,我的功底不行,您越看越不明白了………………(比MSDN解釋的還更糟糕)
Expression<Func<int,int>> exp1 = o=> o + 1; Expression<Action<int>> exp2 = o=> o = 1 + 2;
這種通過 o=> ...... 定義的就是LambdaExpression了。回過頭,我上面說的:
一段包含有運算表達式的代碼,就是LambdaExpression。
這樣子是不是更容易理解了?當然上面只做了 加法操作,當然不僅僅是這些操作,比如:
Expression<Func<UserVO, object>> exp = o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")
再比如在我們的Linq To Object中(當然這里是純委托類型:Func<int,bool>,但它也是lambda表達式(注意不是表達式樹))
var lst = new List<int>(); lst.Where(o => o > 0);
這些都是Lambda的定義。並且我們在上篇中也學習到如何將Expression<Func<UserVO,object>>轉換成Func<UserVO,object>。
這時候聰明的讀者會想,即然我可以直接定義o=>.... ,為什么還要去理解LambdaExpression,反正C#編譯器會把它轉成LambdaExpression,根本不用我們關心。
確實是這樣,如果我們不需要動態構造它
我的意思是在程序運行時動態的生成它,則不是在編寫代碼的時候定義它
的時候,確實不用管是不是LambdaExpression了,只管在代碼上定義就行了。
但是,其實很多場景下,我們是需要動態的構造它的,然后將它傳遞給其它地方,讓他們去解析它,比如說:
場景:在系統分層中,我們有個實體類,比如叫:UserVO(它屬於xxx.Entity類庫)。而在我們的底層中,需要動態對實體類生成一些“通用的”操作(比如邏輯刪除功能)。但是我們知道底層是不知道上層有什么數據類型,甚至被誰調用了也不知道。因此這個時候,我就必須以動態構造的方式來創建它了。(因為我根本不知道有UserVO這個類)
事實上,上面舉的場景例子不僅僅是LambdaExpression,其它的Expression也是如此。
在講動態構造前,我覺得還是先讓大家學習如何解析它,必境我們的學習是先了解它的內部結構,才更好的知道如何構造它,不是嗎?
這要請出我們偉大的ExpressionVisitor類了。事實上,在我的Farseer.Net ORM框架中重新封裝了這個類,叫AbsExpressionVisitor.cs,它是我所有表達式樹訪問器的基類,另外派出了一個專門提供給SQL的解析器,叫AbsSqlVisitor.cs
其中有個入口方法:
protected virtual Expression Visit(Expression exp)
ExpressionVisitor以訪問者模式(設計模式)來訪問這個表達式樹。可以看到有個:exp.NodeType屬性,返回的是:ExpressionType枚舉:
protected virtual Expression Visit(Expression exp) { if (exp == null) return exp; switch (exp.NodeType) { case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.ArrayLength: case ExpressionType.Quote: case ExpressionType.TypeAs: return this.VisitUnary((UnaryExpression)exp); case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.Divide: case ExpressionType.Modulo: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.Equal: case ExpressionType.NotEqual: case ExpressionType.Coalesce: case ExpressionType.ArrayIndex: case ExpressionType.RightShift: case ExpressionType.LeftShift: case ExpressionType.ExclusiveOr: return this.VisitBinary((BinaryExpression)exp); case ExpressionType.TypeIs: return this.VisitTypeIs((TypeBinaryExpression)exp); case ExpressionType.Conditional: return this.VisitConditional((ConditionalExpression)exp); case ExpressionType.Constant: return this.VisitConstant((ConstantExpression)exp); case ExpressionType.Parameter: return this.VisitParameter((ParameterExpression)exp); case ExpressionType.MemberAccess: return this.VisitMemberAccess((MemberExpression)exp); case ExpressionType.Call: return this.VisitMethodCall((MethodCallExpression)exp); case ExpressionType.Lambda: return this.VisitLambda((LambdaExpression)exp); case ExpressionType.New: return this.VisitNew((NewExpression)exp); case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: return this.VisitNewArray((NewArrayExpression)exp); case ExpressionType.Invoke: return this.VisitInvocation((InvocationExpression)exp); case ExpressionType.MemberInit: return this.VisitMemberInit((MemberInitExpression)exp); case ExpressionType.ListInit: return this.VisitListInit((ListInitExpression)exp); default: throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType)); }
然后根據傳入進來的表達式樹,進行一一解析,您能從中看到,當case 到 ExpressionType.Lambda時,會強制轉換成LambdaExpression
並傳入VisitLambda方法中:
protected virtual Expression VisitLambda(LambdaExpression lambda) { Expression body = this.Visit(lambda.Body); if (body != lambda.Body) { return Expression.Lambda(lambda.Type, body, lambda.Parameters); } return lambda; }
事實上,這段代碼不用太過理解其它部份,只需要知道:
當Expression的NodeType == ExpressionType.Lambda時,是可以顯示轉換成:LambdaExpression的。
並且它有一個叫Body的屬性:(獲取 lambda 表達式的主體。),以及一個叫Parameters的屬性:(獲取 lambda 表達式的參數。)
Body返回的是LambdaExpression的主體。
主體:指在LambdaExpression中的主要結構,或者說主要表達式。比如紅色標記部份的:
o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")
而Parameters的返回的是參數表達式樹(出現了一個新名詞,下篇會詳細講解)。這里簡單的講解:上面出現的o 即是參數,o的類型是UserVO
接着上面的:VisitLambda方法里繼續訪問:this.Visit(lambda.Body),也就是解析主體部份。
從上面的表達式代碼(紅色部份),它會執行:this.VisitBinary((BinaryExpression)exp);方法。
在這里知道BinaryExpression(表示包含二元運算符的表達式。)是對於上面的&&的解析就足夠了。
在上篇我們強調了Expression是一種數據結構,也就是說紅色部份,我們定義的代碼實質是被保存成一種數據結構的,如圖:
紅色底是:BinaryExpression類型(表示包含二元運算符的表達式。)二元運算,比如 && || >= < !=
藍色底是:ParameterExpression類型(表示命名的參數表達式。)傳入的參數,比如UserVO實體類
綠色底是:ConstantExpression類型(表示具有常量值的表達式。)具體的常量值。
由於太晚了,這篇末尾的動態構造,放到下一篇中