C#表達式樹的初步瞭解

  在很早之前就據說過表達式樹了,但並無去了解它。雖然本身用過linq to sql和linq to entity,但也就用着就用着,並無去深究c#代碼怎麼會生成sql代碼而不是IL。廢話很少說了,開寫吧!sql

.net裏表達式樹核心概念就是:將代碼做爲數據。它將一些代碼表示爲一個對象樹,樹中的每一個節點自己都是一個表達式,不一樣的表達式類型表明能在代碼中執行不一樣操做:二元操做,一元操做,方法調用等等。express

  System.Linq.Expressions命名空間包含了表明表達式的各個類。全部的表達式類都從Expression類派生,Expression是個抽象類,主要包含的是一些靜態的方法,這些方法用於生成其餘表達式類的實例。Expression類還包含了兩個重要屬性:c#

  1.Type屬性:表明了表達式求值結果的類型。好比,一個表達式是要獲取一個字符串的Length屬性,那麼該表達式的Type屬性應爲int類型spa

  2.NodeType屬性:表明了表達式的種類。這個種類表示成ExpressionType枚舉的一個成員:LessThan,Invoke,Multiply,MemberAccess等等(有80幾種,汗!)。.net

 

一個表達式樹的簡單例子                                                                                                   code

    static void Main(string[] args)
    {
        Expression firstArg = Expression.Constant(2);
        Expression secondArg = Expression.Constant(4);
        Expression add = Expression.Add(firstArg, secondArg);

        Console.WriteLine(add);
    } 

輸出結果:對象

上面的代碼將會生成以下圖的表達式樹:blog

值得注意的是,表達式中「葉」表達式在代碼中是最早建立的:表達式是自下而上構建的。這是由「表達式不易變」這一事實實現的。ip

將表達式樹編譯成委託                                                                                                       字符串

LambdaExpression是從Expression派生的類型。泛型類Expression<TDelegate>是從LambdaExpression派生的,其中泛型參數TDelegate必須是委託類型。

LambdaExpression有個Compile方法能建立恰當類型的一個委託。而Expression<TDelegate>的Compile方法返回TDelegate類型的委託。來看看下面的例子:

    static void Main(string[] args)
    {
        Expression firstArg = Expression.Constant(2);
        Expression secondArg = Expression.Constant(4);
        Expression add = Expression.Add(firstArg, secondArg);

        Expression<Func<int>> func = Expression.Lambda<Func<int>>(add);
        Func<int> compiled = func.Compile();
        Console.WriteLine(compiled());
    }

咱們經過Expression.Lambda<TDelegate>(Expression expression)方法來建立Expression<TDelegate>類型對象,再調用其Compile方法獲取表達式樹編譯出的委託實例。

將C# Lambda表達式轉換成表達式樹                                                                                   

咱們知道Lambda表達式能顯示或隱式地轉換成恰當的委託實例。可是,編譯器也能很輕鬆的將Lambda表達式構建爲一個表達式樹:

//將Lambda表達式轉換成表達式樹
Expression<Func<int>> return5 = () => 5;

可是,並非全部的Lambda表達式都能轉換成表達式樹,有一些限制:不能將帶有一個語句塊的Lambda轉換成一個表達式樹-----只有對單個表達式進行求值得Lambda才能夠。表達式中不能包含賦值操做,由於表達式樹中表示不了這種操做。還有其餘一些較少見的限制,總而言之,若是存在轉換問題,你會在編譯時發現。 

位於Linq核心的表達式樹                                                                                                    

表達式樹能夠說是Linq的核心之一,爲何是Linq的核心之一呢?由於表達式樹使得c#再也不是僅僅能編譯成IL,咱們能夠經過c#生成一個表達式樹,將結果做爲一箇中間格式,在將其轉換成目標平臺上的本機語言。好比SQL。咱們經常使用的Linq to sql就是這樣生成SQL的。

下圖展現了Linq to Objects和Linq to SQL的不一樣路徑:

資料參考於《深刻理解c#》第二版

相關文章
相關標籤/搜索