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


序言

  在上一篇中,我們認識了什么是表達式樹、什么是委托,以及它們的關系。可能是我功力不好,貌似大家都不怎么關注,沒有講解出不同角度的問題。

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

  做程序開發是一群很奇怪的人群,我們居然可以通過密密麻麻的英文字符加上標點符號從中找到樂趣,確實很奇怪。

  考慮到大家接觸新技術需要一個過程:

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

  所以我不打算一上來,就放一大堆代碼,然后解釋這是什么,那是什么,因為會接觸很多新的關鍵詞,這樣大家學習起來會也會很痛苦

  本系列后面的所有篇幅都只在每篇中提一個新概念,這樣大家學習起來可以減少學習的范圍。

  然后也讓大家徹底搞懂每種類型的作用,同時我會用Lambda方式、動態構造、以及表達式樹的結構三種方式來共同研究每篇課題的新類型。

什么是LambdaExpression

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

這段不明白沒關系,看下面示例就知道了

  首先,我們先從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類型(表示具有常量值的表達式。)具體的常量值。

總結

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


免責聲明!

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



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