2010-01-06 40 views
59

IEnumerable<T>的擴展方法之一是.AsEnumerable()。該方法將其被調用的可枚舉對象轉換爲IEnumerable<T>的實例。但是,由於對象必須實現IEnumerable<T>才能適用於此擴展方法,因此轉換爲IEnumerable<T>是一件簡單的事情,可以將其轉換爲IEnumerable<T>。我的問題是爲什麼這種方法存在?爲什麼使用.AsEnumerable()而不是強制轉換爲IEnumerable <T>?

實施例:

List<string> strings = new List<string>() { "test", "test2", "test3" }; 
IEnumerable<string> stringsEnum1 = strings.AsEnumerable(); 
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings; 

在上面的例子,stringsEnum1stringsEnum2是等價的。擴展方法的要點是什麼?

編輯:作爲一個推論,爲什麼在鑄造到IQueryable<T>時有一個.AsQueryable()方法是等價的?

回答

74

可讀性是這裏的主要問題。考慮到

Table.AsEnumerable().Where(somePredicate) 

((IEnumerable<TableObject>)Table).Where(somePredicate). 

更具可讀性或者試想將執行SQL Server上的查詢,並在內存中的其餘部分:

Table.Where(somePredicate) 
    .Select(someProjection) 
    .AsEnumerable() 
    .SomethingElse() 

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate) 
             .Select(someProjection)) 
             .SomethingElse() 

現在,至於爲什麼這樣一種方法在LINQ to SQL DataContext中完全有用,可以考慮Table的示例。由於TableIQueryable它實現IEnumerable。當您在這樣的Table上調用Where方法並枚舉結果時,將執行代碼,最終導致SQL語句在SQL Server上執行。 AsEnumerable做的是說,不,我不想用LINQ to SQL提供程序來執行Where,我想用Where的LINQ to Objects實現。

因此枚舉過

Table.Where(somePredicate) 

導致查詢而枚舉過

Table.AsEnumerable().Where(somePredicate) 

使由Table表示的表裝入存儲器中的SQL服務器上執行,並在存儲器中執行Where功能(而不是在SQL Server上!)

這是點的AsEnumerable:對所有您可以隱藏IEnumerable方法的特定實現,而是使用標準實現。

+1

這是真的 - 但不會投射到'IEnumerable '提供相同的結果嗎? – 2010-01-06 15:18:01

+6

我發現'Table.AsEnumerable()。where(somePredicate)'比'((IEnumerable )Table).Where(somePredicate)'更具可讀性''。 – jason 2010-01-06 15:25:17

+0

我們知道它更具可讀性,但它們是否相等? – 2010-01-06 15:29:56

3

這只是一個最好的和最短的方式投入到IEnumerable。如果您在Reflector中查看它,您可以看到它除了將對象作爲IEnumerable返回外,什麼也不做。

從MSDN:

的AsEnumerable(OF TSource)(IEnumerable的(中TSource)) 方法具有比以 變化從一種類型的工具的編譯時型源極 的其他無影響IEnumerable(Of T)IEnumerable(Of T) 本身。

+0

我不確定這是否屬實。我相信這是與查詢實現。 – gingerbreadboy 2010-01-06 15:19:03

+0

@runrunraygun:這是因爲它更容易從IQueryable切換到IEnumerable,但它通常不會做任何特殊的事情,除非將它轉換爲IEnumerable。如果你不相信我,請檢查Reflector ;-) – 2010-01-06 15:32:26

+0

@runrunraygun:我提供了一個來自你提供的證明這個鏈接的報價;-) – 2010-01-06 15:39:51

2

正如你所說,如果一個類型已經實現了IEnumerable<T>那麼在向接口強制轉換或調用AsEnumerable方法之間沒有任何功能差異。

我的猜測,這只是一個猜測,就是調用AsEnumerable提高了可讀性和保留的其他LINQ擴展方法的流暢簽名:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty); 

// vs 

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty); 

它還允許不執行IEnumerable<T>類型 - for example, DataTable - 有自己的版本AsEnumerable擴展。這允許您在針對這些類型的查詢中繼續使用相同的模式 - 即使它是您調用的不同方法AsEnumerable - 無需擔心類型是否真的實現IEnumerable<T>

+0

但是那些沒有實現IEnumerable 的類型不能訪問AsEnumerable擴展方法 - 所以這些類型不包含在這個問題所關注的類型集合中。儘管如此,好點。 – 2010-01-06 15:37:32

+0

@Erik:我的觀點是你可以在查詢DataTable時使用*相同的模式*,例如,即使'DataTable'沒有實現IEnumerable ':'var query = yourDataTable.AsEnumerable()。選擇(x => x [「YourColumn」])等。http://msdn.microsoft.com/en-us/library/system.data.datatableextensions.asenumerable.aspx – LukeH 2010-01-06 15:45:42

+0

是的 - 這就是爲什麼我投票給你。 =) – 2010-01-06 15:47:00

13

我想過除了可讀性之外的一個原因,儘管與查詢實現有關:在通過另一個Linq提供程序返回的匿名類型上使用Linq到對象。您不能轉換爲匿名類型(或一系列匿名類型),但您可以使用.AsEnumerable()爲您執行演員陣容。

例子:

// Get an IQueryable of anonymous types. 
var query = from p in db.PeopleTable /* Assume Linq to SQL */ 
      select new { Name = p.Name, Age = p.Age }; 

// Execute the query and pull the results into an IEnumerable of anonymous types 
var enum = query.AsEnumerable(); 

// Use Linq to Objects methods to further refine. 
var refined = from p in enum 
       select new 
       { 
        Name = GetPrettyName(p.Name), 
        DOB = CalculateDOB(p.Age, DateTime.Now) 
       }; 

顯然這裏的原因是,我們想使用類似的LINQ to SQL的一些記錄下拉到一個匿名類型,然後執行一些自定義邏輯(這將是不可能的通過Linq to SQL)在客戶端使用Linq to Objects。

鑄造到IEnumerable<_anon>是不可能的,所以.AsEnumerable()是唯一的出路。

謝謝大家誰回答幫我把這件作品放在一起。 =)

2

匿名類型是提供這些類型的擴展方法的主要原因。 (您不能在泛型參數中使用匿名類型) 但方法調用可以使用類型推斷,從而可以省略在泛型參數中指定類型。

2

如果在與Linq擴展方法具有相同名稱的對象上存在方法,它將隱藏擴展方法。使用AsEnumerable可以讓你獲得擴展名。

這似乎是SP1中的新增功能。

昨天我做了一個一行代碼,提取成員標識符從數據表: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId) 
); 

這工作得很好,直到我安裝SP1。現在,它不會工作,除非它讀取

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId) 
); 

編輯:我找到了真正的原因

它是如此,你可以同時使用遠程方法(例如在一個SQL語句)和本地方法相同的linq語句。如果不使用AsEnumerable(即只是強制轉換),它將使查詢生成器嘗試爲包含本地方法的遠程執行創建一個表達式樹。將AsEnumerable放入查詢將導致該查詢的其餘部分在遠程查詢的結果上本地執行。

https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

表示數據庫表的表型可以有一個Where方法,是以謂詞參數作爲表達式樹並轉換樹到SQL遠程執行。如果不需要遠程執行,例如因爲謂詞調用本地方法,則可以使用AsEnumerable方法來隱藏自定義方法,並使標準查詢運算符可用。

3

因爲我在看書C# 6.0 in a Nutshell。下面是書中的一個例子AsEnumerable


目的是鑄造一個IQueryable<T>序列IEnumerable<T>,迫使隨後的查詢運算符來結合可枚舉運營商,而不是可查詢運算符。這會導致查詢的其餘部分在本地執行

爲了說明這一點,假設我們在SQL Server中有一個MedicalArticles表,並且希望使用LINQ to SQL或EF來檢索所有關於流行病的摘要包含少於100個單詞的文章。對於後者斷言,我們需要一個正則表達式:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b"); 

var query = dataContext.MedicalArticles 
      .Where (article => article.Topic == "influenza" && 
      wordCounter.Matches (article.Abstract).Count < 100); 

的問題是,SQL Server不支持正則表達式,所以LINQ到DB提供程序會拋出異常,抱怨查詢不能轉換爲SQL。首先通過LINQ檢索流感的所有文章,SQL查詢,然後不到100個字的摘要本地過濾:我們可以通過查詢分兩步解決這個

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b"); 

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles 
    .Where (article => article.Topic == "influenza"); 

IEnumerable<MedicalArticle> localQuery = sqlQuery 
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100); 

隨着AsEnumerable,我們可以這樣做在單個查詢中:

var query = dataContext.MedicalArticles 
     .Where (article => article.Topic == "influenza") 
     .AsEnumerable() 
     .Where (article => wordCounter.Matches (article.Abstract).Count < 100); 

調用AsEnumerable的另一種方法是調用ToArray或ToList。 AsEnumerable的優點是它不會強制立即執行查詢,也不會創建任何存儲結構。

相關問題