LINQ入門(中篇)

來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=163
GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.comphp

到如今爲止你還未觸碰LINQ,那進來吧 —— LINQ入門(中篇)
前 言
  在上篇中簡單的分享了LINQ的基礎概念及基礎語法,若是沒有閱讀過上篇的朋友能夠點擊這裏。感謝你們的支持,本篇咱們將更進一步的學習LINQ的一些相關特性及應用方法。廢話很少說,請往下閱讀吧。
延遲加載
  在上篇中簡單的和你們提到了LINQ具備一個頗有意思的特性那就是「延遲加載」(或「延遲計算」),什麼是延遲加載呢?先看來自官方的描述:延遲執行意味着表達式的計算延遲,直到真正須要它的實現值爲止。是否是以爲有點生澀難理解呢?按照我我的的理解通俗的講就是,每當咱們編寫好一段LINQ表達式時,此時這個表達式所表明的序列變量僅僅只是一個代理,編譯器在執行編譯時根本就不鳥這段代碼,檢查完語法正確性後直接跳過,直到代碼在編譯器動態運行序列變量在其餘代碼塊被調用時,它所代理的linq表達式纔會執行。啊~~看到這裏你是否是要暈了,到底要怎麼理解啊,無廢話上代碼:sql

1 // 已知一個序列
 2 var array = new int[] {1, 2, 3};
 3 
 4 // 編寫一段LINQ表達式得到一個序列變量query
 5 // 注意,這個變量僅僅是一個代理,在執行編譯的時候,編譯器檢查完
 6 // 該代碼的正確性後直接跳過,再也不理會
 7 var query = from arr in array
 8             where arr > 1
 9             select arr;
10 
11 // 調用上述序列變量query,此時上述的LINQ表達纔會執行。注意此時已經是在
12 // 編譯器Runtime 的狀況下執行
13 foreach(var q in query)
14     Console.WriteLine(q.ToString());

若是你以爲上述例子不能讓你有個深入的理解,那麼請看來自MSDN的例子express

1 public static class LocalExtensions
 2 {
 3     public static IEnumerable<string>
 4       ConvertCollectionToUpperCase(this IEnumerable<string> source)
 5     {
 6         foreach (string str in source)
 7         {
 8             Console.WriteLine("ToUpper: source {0}", str);
 9             yield return str.ToUpper();
10         }
11     }
12 }
13 
14 class Program
15 {
16     static void Main(string[] args)
17     {
18         string[] stringArray = { "abc", "def", "ghi" };
19         // 這裏方法 ConvertCollectionToUpperCase 是不會在編譯時進行調用覈查的,直到下面的foreach調用變量 q 此方法纔會執行
20         var q = from str in stringArray.ConvertCollectionToUpperCase()
21                 select str;
22 
23         foreach (string str in q)
24             Console.WriteLine("Main: str {0}", str);
25     }
   }

注意,ConvertCollectionToUpperCase 是一個靜態擴展方法,後續講解,若是你對.net 2.0 的 yeild 不熟悉的網上查閱吧,這裏就不作介紹了。網絡

// 輸出結果
// ToUpper: source abc
// Main: str ABC
// ToUpper: source def
// Main: str DEF
// ToUpper: source ghi
// Main: str GHI運維

  小結,延遲加載有好也有壞,因爲是在Runtime的狀況下執行序列,因此就容易形成未知異常,斷點打錯等等,因此編碼LINQ是必定要考慮到它的這個特性。
lambda 表達式
  瞭解完延遲加載後,那麼如今咱們須要簡單的學習一下.net 3.5 給咱們帶來的新特性lambda表達式,在上篇的評論中,有園友問lambda和linq有什麼關係,在這裏其實他們沒有任何關係,是徹底不一樣的東西,可是咱們爲何要掌握它呢?由於在後續的學習中會使用大量的lambda表達,他可使咱們的代碼更優雅更有可讀性,大大提升了咱們的編碼效率。
  那麼在學習lambda以前,先來回顧一下.net 2.0給咱們帶來的委託 delegate ,這個你必定不會感到陌生吧,並且必定會經常使用他。對於委託這裏就不作詳細的介紹了,要複習委託的在網上查閱吧。經過委託,咱們能夠獲得一個東西「匿名方法」。咦,是否是以爲很眼熟,呵呵,用代碼來加深回憶吧ide

public delegate void SomeDelegate1;
public delegate void SomeDelegate2(arg 1, arg 2);

// 匿名方法
SomeDelegate1 del1 += delegate() {...};
SomeDelegate2 del2 += delegate(arg1, arg2) {...}

上面的代碼中咱們看到在.net 2.0時代,咱們能夠經過delegate建立匿名方法,提升編碼的靈活性,那麼lambda和這個有什麼關係呢,lambda對匿名方法進行了昇華。看代碼:函數

public delegate void SomeDelegate1;
public delegate void SomeDelegate2(arg 1, arg 2);

// 匿名方法
SomeDelegate1 del1 += () => {...};
SomeDelegate2 del2 += (arg1, arg2) => {...}

呵呵,是否是以爲有點難以想象呢,言歸正傳什麼是lambda表達式呢,來自官方的定義:「Lambda 表達式」是一個匿名函數,它能夠包含表達式和語句,而且可用於建立委託或表達式樹類型。全部 Lambda 表達式都使用 Lambda 運算符 =>,該運算符讀爲「goes to」。 該 Lambda 運算符的左邊是輸入參數(若是有),右邊包含表達式或語句塊。 Lambda 表達式 x => x x 讀做「x goes to x times x」。在定義裏提到了表達式樹,這是高階晉級的話題,這裏就不作討論了,咱們先把精力放在入門與實戰應用上。
常規的lambda表達式以下:
(parameters) => {expression}
當指定的委託類型沒有參數是表達式能夠以下
() => {expression} 例:() => {/
執行某些方法*/}
若是表達右側花括號裏只有一個表達例如一元表達式,二元表達式等等,又或者是一個方法時那麼花括號能夠省略以下:
(x) => x; // 最簡表達式學習

(x, y) => x == y;網站

() => SomeMethod();
注意,若是右側的表達式存在花括號"{}",並且委託是具備返回類型的,那麼表達式必須帶上 return 關鍵字,以下:
(x, y) => {return x == y;};
到此咱們已對lambad 表達式有了必定的掌握與瞭解。那麼咱們擴展一下,在.net 3.5中,ms 給咱們提供了兩個泛型委託 分別是 Fun<T> 和 Action <T> 他們能夠幫助咱們省去了返回建立經常使用委託的麻煩,提升編碼效率。
共同點:它們至多提供委託傳遞6個參數(任意類型);
不一樣點:Fun 要求必須具備返回類型,而Action則必須不返回類型,規定返回 void
示例:
Fun<int, int, bool> fun = (a, b) => a==b;this

Action<string> action = (p) => Console.Write(p);
小結,lambda 對我我的而言是個又愛又恨啊,不過愛多一點,它使咱們寫更少的代碼作更多的事,可是在調試時一旦修改表達式內容,那麼當前調試要麼中止,要麼從新開始,ms要是在這方面作得更完美些就好啦。不過它也間接提醒咱們要有好的編碼設計思惟。
靜態擴展方法
  說完lambda,那麼咱們就進一步瞭解一下.net 3.5的另外一個新特性「靜態擴展方法」,什麼是靜態擴展方法呢,官方定義:擴展方法使您可以向現有類型「添加」方法,而無需建立新的派生類型、從新編譯或以其餘方式修改原始類型。 擴展方法是一種特殊的靜態方法,但能夠像擴展類型上的實例方法同樣進行調用。簡單的說就是咱們能夠向一個已知的類型在不經過繼承,複寫等操做的狀況下添加一個方法,以便類型的實例能夠直接使用該方法。示例以下:

static class A
{
    // 這裏的p1,p2 僅做爲示例,實際中咱們不必定須要
    public static int ExtendMethod1(this string input,string p1, string p2)
    {
         return int.Parse(input + p1 + p2);
    }

     // 泛型方法
     public static TOutput, ExtendMethod2<TOutput>(this object obj);
     {
           return (TOutput)obj;
     }
}

class B
{
  void Main()
   {
      var a = "1";
      var result = a.ExtendMethod1("2","3");
      // result:123
   }
}

注意,方法的 static 是必須的,並且需在靜態類裏。第一個參數 this 是必須的,緊跟着 this 後面的須要擴展的類型實例參數,也是必須的。至於後面的方法調用傳遞參數就因我的所需了。
  既然咱們學習了靜態擴展方法,那麼它和LINQ又有什麼關係呢?在System.Linq的命名空間中提供了大量的靜態擴展方法,這些靜態方法自己就對linq表達式的封裝,這樣咱們就能夠省去了編寫簡單的linq表達式的步驟。以下:

var array = new int[]{1,2,3,4,5};

var query1 = from arr in array
             select arr;

var query2 = array.Select(e => e);

上面的示例中 query1 和 query2 是等價的,經過 query2 咱們是否是又能夠偷懶了不少,呵呵。
再來點帶where的

var array = new int[]{1,2,3,4,5};

var query1 = from arr in array
             where arr > 2
             select arr;

var query2 = array.Where(e => e > 2);

再來一個複合型的
var array = new int[]{1,2,3,4,5};

var max = (from arr in array.Where(e => e > 2)
           select arr).Max();

是否是以爲很cool。因爲篇幅的關係在這裏就不逐一的去接受這些靜態方法了,下面是一些經常使用的靜態方法列表,感興趣的去MSDN查閱詳細吧。
Aggregate , All , Any , AsEnumerable , Average , Cast , Concat , Contains, Count, DefaultIfEmpty , Distinct , ElementAt, ElementAtOrDefault ,Empty , Except , First, FirstOrDefault , GroupBy , GroupJoin , Intersect , Join , Last , LastOrDefault , LongCount , Max , Min , OfType ,OrderBy ,OrderByDescending , Range , Repeat , Reverse , Select , SelectMany , SequenceEqual , Single , SingleOrDefault , Skip , SkipWhile , Sum ,Take, TakeWhile , ThenBy , ThenByDescending , ToArray , ToDictionary , ToList, ToLookup, Union,Where
Cast<T> 和 OfType<T> 靜態擴展方法
  在最後咱們仍是要注意兩個經常使用的靜態方法Cast<T>, OfType<T> 。它們的共同點是都能把非IEnumerable<T> 類型的集合轉換成IEnumerable<T>類型,而後再
進行LINQ操做,以下

var dt = new DataTable();
dt.Columsn.Add("A", typeof(int));

var newRow1 = dt.NewRow();
newRow1["A"] = 1;

var newRow2 = dt.NewRow();
newRow2["A"] = 2;

dt.Rows.Add(newRow1);
dt.Rows.Add(newRow2);

var query1 = dt.Rows.Cast<DataRow>().Select(e=>(int)e["A"]);
var query2 = dt.Rows.OfType<DataRow>().Select(e=>(int)e["A"]);

這樣咱們就能夠獲得看上去兩個相同的序列,在這裏要注意:MSDN上的說明存在誤導,MSDN對於OfType<T>的解釋存在誤差,實際上經本人反覆敲代碼驗證,獲得的結論是,Cast對序列進行強制轉換,一旦轉換不成功則拋出異常。OfType則是一旦轉換不成功,則不會拋出異常,可是將會獲得一個空序列。見下面代碼:

var arr1 = new string[] { "1","2","test" };
var arr2 = arr1.Cast<int>();
var arr3 = arr1.OfType<int>();

//經過Cast轉換,則會拋出異常
foreach (var i in arr2)
     Console.WriteLine(i.ToString());

//經過OfType轉換,有異常可是不會拋出並獲得一個空序列
foreach (var i in arr3)
      Console.WriteLine(i.ToString());

Console.Read();

總 結
  本文到此,咱們已對LINQ涉及的應用有了進一步的瞭解。學習什麼是linq的延遲加載,lambda和linq是否有曖昧關係。以及靜態擴展方法對linq的輔助做用。也許你會問既然能夠用靜態擴展方法替代編寫linq,那麼兩者怎麼擇取呢,據磚家叫獸提議咱們應該先以linq命名空間下的靜態擴展方法爲主,實在是很複雜的linq表達式,咱們再考慮使用linq自己的表達式編寫。後續咱們將分享學習LINQ更貼近實戰應用的知識,linq to dataset, linq to xml, linq to sql, linq to entities.
  感謝您的閱讀,若是有說得不對的地方請指正。

來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=163
GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.com

相關文章
相關標籤/搜索