天天看點

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

前言

  建議29、差別LINQ查詢中的IEnumerable<T>和IQueryable<T>

  建議30、使用LINQ取代集合中的比較器和疊代器

  建議31、在LINQ查詢中避免不必要的疊代

建議29、差別LINQ查詢中的IEnumerable<T>和IQueryable<T>

  LINQ查詢方法一共提供了兩類擴充方法,在System.Linq命名空間下,有兩個靜态類:

    Enumerable類,它針對繼承了IEnumerable<T>接口的集合類進行擴充。

    Queryable類,它針對繼承了IQueryable<T>接口的集合類進行擴充。

稍加觀察我們會發現,接口IQueryable<T>實際也是繼承了IEnumerable<T>接口的,是以緻使這兩個接口額方法在很大成都上是一緻的。簡單的來表述就是:本地資料源用IEnumerable<T>,遠端資料源用IQueryable<T>。

  LINQ查詢從功能上來講實際上可以分為三類:LINQ to OBJECTS、LINQ to  SQL、LINQ to XML。設計Enumerable<T>和Queryable<T>兩套接口的原因是為了差別對待LINQ to OBJECTS、LINQ to SQL,兩者對于查詢的處理在内部使用的是完全不同的機制。針對LINQ to OBJECTS時,使用Enumerable中的擴充方法對本地集合進行排序和查詢等操作,查詢參數接受的是Func<>。Func<>叫做謂語表達式,相當于一個委托。針對LINQ to SQL時,則使用Queryable中的擴充方法,它接受的參數是Expression<>。Expression<>用于包裝Func<>。LINQ to SQL引擎最終會将表達式樹轉化成為相應的SQL語句,然後在資料庫中執行。

  那麼到底什麼時候使用IQueryable<T>,什麼時候使用IEnumerable<T>呢?我們來簡單的看一個例子:

通過上面的代碼可以發現,雖然我們針對temp1使用的是延遲求值,但是在整個LINQ查詢語句的最後對結果使用了AsEnumerable方法,這相當于将遠端數組轉成了本地資料。通過資料庫的見識工具也可以驗證這一點。

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

現在來看另外一個查詢,其實還是上面的查詢隻是做了簡單的修改

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

通過監控可以發現它是組合兩個查詢語句,而生成了一條SQL,如果不了解這一點,那麼在編寫程式時将會造成性能損耗。在LINQ to SQL的查詢中,要盡量始終使用IQueryable<T>。

在使用IQueryable<T>和IEnumerable<T>的時候還需要注意一點,IEnumerable<T>查詢的邏輯可以直接用我們自己所定義的方法,IQueryable<T>則不能使用自定義的方法,它必須先生成表達式樹,查詢由LINQ to SQL引擎處理。在使用IQueryable<T>查詢的時候,如果使用自定義的方法,則會抛出異常。

建議30、在查詢中使用Lambda表達式

可以發現以上方式實作的排序至少存在兩個問題:

1)可擴充性太低,如果存在新的排序要求,就必須實作新的比較器。

2)對代碼的侵入性太高,為類型繼承了接口,增加了新的 方法。

那麼有沒有一種方法,即使類型隻存在自動實作的屬性,也能滿足多方面的排序要求呢?答案是使用LINQ。LINQ提供了類似于SQL的文法來實作周遊、篩選與投影集合的功能。借助于LINQ的強大功能。

 來看使用LINQ之後的代碼:

執行結果如下:

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

我們可以利用LINQ強大的功能來簡化自己的編碼,但是LINQ功能的實作本身就是借助于FCL泛型集合的比較器、疊代器、索引器的。LINQ相當于封裝了這些功能,讓我們使用起來更加的友善。在命名空間System.Linq下存在很多靜态類,這些靜态類存在的意義就是FCL的泛型集合提供擴充方法。

強烈建議你利用LINQ所帶來的便捷性,但我們仍需要掌握比較器、疊代器、索引器的原理,以便更好地了解LINQ的思想,寫出更高執行的代碼。

建議31、在LINQ查詢中避免不必要的疊代

 無論是SQL查詢還是LINQ查詢,搜尋到結果立刻傳回總比搜尋完所有的結果再将結果傳回的效率要高。現在簡單來建立一個自定義的集合類型來說明。

簡單的進行調用

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

通過結果發現,第二種的性能明顯比第一種好很多。第一種查詢疊代了4次,而第二種僅有1次。

第二種查詢僅僅疊代1次是因為25正好放在list的首位,而查詢條件是大于等于20.First方法實際完成的工作就是:搜尋到滿足條件的第一個元素,就從集合中傳回。如果沒有符合條件的元素,它也會周遊整個集合。

 與First方法類似的還有Take方法,Take方法接收一個整型參數,然後為我們傳回該參數指定的元素個數。與First一樣,它滿足條件以後,會從目前的疊代過程直接傳回,而不是等到整個疊代過程完畢再傳回。如果一個集合包含了很多的元素,那麼這種查詢會為我們帶來可觀的時間效率。

再來看下面的例子,雖然LINQ查詢的最後結果都是傳回包含了兩個元素"Niki"對象,但是實際上,使用Take方法僅僅為我們疊代了2次,而使用where查詢方式帶來的确實整個集合的疊代,首先修改一下集合類中的元素

調用

結果

編寫高品質代碼改善C#程式的157個建議[IEnumerable<T>和IQueryable<T>、LINQ避免疊代、LINQ替代疊代]

在實際的編碼過程中,要充分運用First和Take等方法,這樣才能為我們的應用帶來高效性,而不會讓時間浪費在一些無效的疊代中。

英語小貼士

1、Where can I get my baggage?——我在那裡可以取得我的行李?

2、I can'find my baggage.——我找不到我的行李。

3、Please wait for a moment while we are investigating.——我們正在調查,請稍等一下。

4、Here is my claim tag.——這是我的行李票。

5、We may have lost some baggage so we'd like to make a lost baggage report.

  Would you come with me to the office?——我們可能遺失了幾件行李,是以必須填份行李遺失報告。請和我到辦公室?

6、Could you please check it urgently?——是否可麻煩緊急查詢?

7、How soon will I find out?——多快可找到?

感謝您的閱讀,如果您對我的部落格所講述的内容有興趣,那不妨點個推薦吧,謝謝支援:-O。