什麼是表達式樹,它與表達式、委託有什麼區別?(1)

序言

  首先,須要普及下基礎知識:html

  Expression咱們稱之爲:表達式樹,數據庫

  而Func<>或者Action 稱之爲:匿名委託,Func與Action的區別是Func帶返回值(至少一個參數),Action不帶返回值(能夠沒有任何參數)。緩存

  以上的關鍵詞是在.net 3.5以後出現的,配合Linq中Lambda使用。服務器

  固然Expression還能夠動態的進行構造它,而不使用Lambda表達式來定義。數據結構

  補充:在CSDN上找到一篇文章講的是利用CodeDOM來生成,以爲有意思,分享下:地址函數

使用場景
  1. 你須要優雅的表達式
  2. 你可能須要將某種帶邏輯性的代碼轉成其它表現形式,好比(轉成string或數據庫的SQL腳本)。這會比你拼接string,顯的更優雅。
  3. 你可能須要制定一個動態的規則,讓客戶端調用。
什麼是表達式樹

  它是一種數據結構體,用於存儲須要計算、運算的一種結構。這種結構能夠只是」存儲「,而不進行運算。性能

  就好像咱們寫了一個方法函數,而不調用它同樣。可是這種結構,咱們是能夠在程序在運行時,進去動態改變它的。而咱們的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 xx 的使用

  解析完了上面所說的,回過頭來咱們在想一想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開發者都須要學習並精通它。

相關文章
相關標籤/搜索