首先,須要普及下基礎知識:html
Expression咱們稱之爲:表達式樹,數據庫
而Func<>或者Action 稱之爲:匿名委託,Func與Action的區別是Func帶返回值(至少一個參數),Action不帶返回值(能夠沒有任何參數)。緩存
以上的關鍵詞是在.net 3.5以後出現的,配合Linq中Lambda使用。服務器
固然Expression還能夠動態的進行構造它,而不使用Lambda表達式來定義。數據結構
補充:在CSDN上找到一篇文章講的是利用CodeDOM來生成,以爲有意思,分享下:地址函數
它是一種數據結構體,用於存儲須要計算、運算的一種結構。這種結構能夠只是」存儲「,而不進行運算。性能
就好像咱們寫了一個方法函數,而不調用它同樣。可是這種結構,咱們是能夠在程序在運行時,進去動態改變它的。而咱們的Func一旦定義好編譯後是沒法更改的。學習
一般表達式樹是配合委託一塊兒的,好比:Expression<Func<int,int>>。this
雖然博主一直想以簡單明瞭的說明去描述它,但感受越描越黑。。。。spa
僅僅是拿Expression來描述它的概念以爲仍是不易讓你們通俗易懂,因此我仍是把它跟Func<>一塊兒描述吧:
首先Expression<Func<>>是能夠轉成Func的。反過來則不行。咱們能夠理解爲Func<>通過定義後,就沒法改變它了。而表達式樹(Expression<Func<>>則是能夠進行變動的。
其次Expression<Func<>>如開頭描述,僅僅只是一種數據結構,或者說載體。它自己並無運算、計算的能力。若是須要這些」能力「,則必須轉爲Func<>才行。
或者咱們能夠這樣認爲:Expression<Func<>>是變量,而Func<>是」方法函數「。
二者均可以經過Lambda語法進行定義,好比:
1 Expression<Func<UserVO, object>> exp = o => o.ID > 0 && o.UserName == "farseer.net"; 2 Func<UserVO, object> fun = o => o.ID > 0 && o.UserName == "farseer.net";
可是,Func一旦定義是沒法在運行時改變它的,固然我這裏說的改變是動態構造,而不是從新定義(賦值)。而表達式樹是能夠的。
好比上面的exp變量改變成:
Expression<Func<UserVO, object>> exp = o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")
而且,你沒法在運行時知道Func的內部是什麼,或者說它在作什麼運算。Func的目地只是提供.net運算時的計算。
精髓:在你須要把這種表達式,轉換成另一種形式時,必須使用Expression。
也能夠狹義的理解爲,比如Func是閉源的,你拿到別人的dll,沒法改變它,只能調用它,而Expression比如你拿到了開源的代碼,你隨時能夠更改它內部的結構。
好比:在個人Farseer.Net中,須要開發者經過傳入上面的exp變量時,Farseer.Net能把它轉換成SQL:
1 select * from UserVO where ID > 0 and UserName = 'farseer.net'
由於Expression是能夠在運行時,分析它的數據結構。而Func是不能夠的。(這裏能夠理解爲Func是被」編譯「的。)
所以,在你須要把.net的某些運算,轉換成其它表達形式,傳輸到其它進程、服務器、文件、數據庫時,必須使用表達式樹。由於它是容許經過代碼進行動態解析的。
解析完了上面所說的,回過頭來咱們在想一想Linq To Sql 跟Linq To Object時,裏面的 Where方法的形參是如何定義的:
Linq To Object:
1 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
Linq To Sql:
1 public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
因此,如今咱們知道爲何IQueryable.Where 用的是:Expression<Func<TSource, bool>> predicate參數了。(由於須要轉換成數據庫所能識別的SQL。)
而若是咱們是本地變量(Linq To Object)時,是這樣操做的:
1 var lst = new List<int>(); 2 lst.Where(o => o > 0);
注意lst.Where傳入的是Func。而不是Expression,由於這裏的計算直接用.net framework來完成。而不須要轉換成其它語言(好比T-SQL)。
由於本系列主要講的是表達式樹,而不是委託,但你們容易把表達式樹與委託搞混淆,或者不清楚他們的區別,咱們先經過這一篇來描述他們是什麼,以及在什麼場景下使用到。
固然,在咱們定義好一個委託時,咱們能夠當即調用它:
1 Func<int, bool> fun = o => o > 0; 2 // result = true,由於傳入100是大於0的 3 bool result = fun(100);
而若是是表達式樹,也能夠像委託那樣調用,不過咱們必須經過調用Compile()轉成對應的委託後纔可:
1 Expression<Func<int, bool>> fun = o => o > 0; 2 // result = true,由於傳入100是大於0的 3 bool result = fun.Compile()(100);
看看Compile的註釋:
1 /// <summary> 2 /// 以表達式目錄樹的形式將強類型 lambda 表達式表示爲數據結構。此類不能被繼承。 3 /// </summary> 4 /// <typeparam name="TDelegate"><see cref="T:System.Linq.Expressions.Expression`1"/> 表示的委託的類型。</typeparam> 5 public sealed class Expression<TDelegate> : LambdaExpression 6 { 7 /// <summary> 8 /// 將表達式樹描述的 lambda 表達式編譯爲可執行代碼,並生成表示該 lambda 表達式的委託。 9 /// </summary> 10 /// 11 /// <returns> 12 /// 一個 <paramref name="TDelegate"/> 類型的委託,它表示由 <see cref="T:System.Linq.Expressions.Expression`1"/> 描述的已編譯的 lambda 表達式。 13 /// </returns> 14 public new TDelegate Compile();
經過本篇,簡單瞭解了表達式樹與委託的區別,後面的篇幅中咱們核心講表達式樹。
其實真要完整的描述清楚表達式樹及其使用,單靠幾篇博文來描述是很難的。但本着分享本身的學習心得態度來跟你們分享。
表達式樹對於開發者來講是很是重要的一項技術,若是沒有表達式樹的出現,在之前性能、計算過程的緩存只能靠Emit來實現。(表達式樹實質也是由Emity實現的。)
因此推薦全部.net開發者都須要學習並精通它。