什麼是LambdaExpression,如何轉換成Func或Action(2)

序言

  在上一篇中,咱們認識了什麼是表達式樹、什麼是委託,以及它們的關係。多是我功力很差,貌似你們都不怎麼關注,沒有講解出不一樣角度的問題。html

  學習一種新技術,是枯燥的過程,只有在你掌握後並能運用時才能從它身上獲得樂趣。git

  作程序開發是一羣很奇怪的人羣,咱們竟然能夠經過密密麻麻的英文字符加上標點符號從中找到樂趣,確實很奇怪。github

  考慮到你們接觸新技術須要一個過程:express

其實對於不少人來講已經不是新技術了,不過您會耐心看本篇後續的文章,說明您可能對這一項技術運用的並非很熟練設計模式

  因此我不打算一上來,就放一大堆代碼,而後解釋這是什麼,那是什麼,由於會接觸不少新的關鍵詞,這樣你們學習起來會也會很痛苦數據結構

  本系列後面的全部篇幅都只在每篇中提一個新概念,這樣你們學習起來能夠減小學習的範圍。框架

  而後也讓你們完全搞懂每種類型的做用,同時我會用Lambda方式、動態構造、以及表達式樹的結構三種方式來共同研究每篇課題的新類型。ide

什麼是LambdaExpression

  LambdaExpression是繼承自Expression的。LambdaExpression的具體表現是:Expression<Func<>>或者Expression<Action<>學習

這段不明白不要緊,看下面示例就知道了this

  首先,咱們先從MSDN上看它的註釋說明:

描述一個 lambda 表達式。 這將捕獲與 .NET 方法體相似的代碼塊。

  看MSDN註釋,咱們仍是沒搞懂它是什麼意思,通俗的講:一段包含有運算表達式的代碼,就是LambdaExpression。

  好吧,我說了吧,個人功底不行,您越看越不明白了………………(比MSDN解釋的還更糟糕)

LambdaExpression的定義
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>。

LambdaExpression有什麼用?

   這時候聰明的讀者會想,即然我能夠直接定義o=>.... ,爲何還要去理解LambdaExpression,反正C#編譯器會把它轉成LambdaExpression,根本不用咱們關心。

   確實是這樣,若是咱們不須要動態構造它

個人意思是在程序運行時動態的生成它,則不是在編寫代碼的時候定義它 

  的時候,確實不用管是否是LambdaExpression了,只管在代碼上定義就好了。

  可是,其實不少場景下,咱們是須要動態的構造它的,而後將它傳遞給其它地方,讓他們去解析它,好比說:

 場景:在系統分層中,咱們有個實體類,好比叫:UserVO(它屬於xxx.Entity類庫)。而在咱們的底層中,須要動態對實體類生成一些「通用的」操做(好比邏輯刪除功能)。可是咱們知道底層是不知道上層有什麼數據類型,甚至被誰調用了也不知道。所以這個時候,我就必須以動態構造的方式來建立它了。(由於我根本不知道有UserVO這個類)

  事實上,上面舉的場景例子不只僅是LambdaExpression,其它的Expression也是如此。

  在講動態構造前,我以爲仍是先讓你們學習如何解析它,必境咱們的學習是先了解它的內部結構,才更好的知道如何構造它,不是嗎?

LambdaExpression的解析

  這要請出咱們偉大的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類型(表示具備常量值的表達式。)具體的常量值。

總結

因爲太晚了,這篇末尾的動態構造,放到下一篇中

相關文章
相關標籤/搜索