轉換操做符是用來實現將輸入對象的類型轉變爲序列的功能。名稱以「As」開頭的轉換方法可更改源集合的靜態類型但不枚舉(延遲加載)此源集合。名稱以「To」開頭的方法可枚舉(即時加載)源集合並將項放入相應的集合類型。sql
全部實現了IEnumerable<T>接口的類型均可以調用此方法來獲取一個IEnumerable<T>集合。此方法通常僅用於實現類中的方法與IEnumerable<T>接口方法重名時。例如,實現類Test中有一個Where方法,當使用Test對象調用Where時,將執行Test自身的Where方法過程。若是要執行IEnumerable<T>的Where方法,即可以使用AsEnumerable進行進行轉換後,再調用Where方法便可。固然,將實現類Test隱式轉換爲IEnumerable<T>接口,再調用接口的Where方法也能達到一樣的效果。如下的代碼演示了這一過程:數組
class AsEnumerableTest<T> : List<T> { public void Where(Func<T, bool> func) { Console.WriteLine("AsEnumerableTest的Where方法"); } } public static void AsEnumerable() { AsEnumerableTest<int> q = new AsEnumerableTest<int>() { 1,2,3,4 }; q.Where(r => r < 3); //q.AsEnumerable().Where(r => r < 3); //IEnumerable<int> i = q; //i.Where(r => r < 3); }
Cast<T> 方法經過提供必要的類型信息,可在IEnumerable(非泛型)的派生對象上調用Cast<T> 方法來得到一個IEnumerable<T>對象。例如,ArrayList 並不實現 IEnumerable<T>,但經過調用 ArrayList 對象上的 Cast<T>(),就能夠使用標準查詢運算符查詢該序列。函數
若是集合中的元素沒法強制轉換爲 T 類型,則此方法將引起異常。如下代碼演示了這一過程:this
ArrayList array = new ArrayList(); array.Add("Bob"); array.Add("Jack"); array.Add(1); foreach (var item in array.Cast<string>()) { Console.WriteLine(item); }
運行此代碼,能夠輸出「Bob」、「Jack」,而後會報出一個異常「沒法將int強制轉換爲string」,這說明Cast方法也是延遲執行實現的,只有在枚舉過程當中纔將對象逐個強制轉換爲T類型。spa
OfType <T> 方法經過提供必要的類型信息,可在IEnumerable(非泛型)的派生對象上調用OfType <T> 方法來得到一個IEnumerable<T>對象。執行OfType<T>方法將返回集合中強制轉換類型成功的全部元素。也就是說,OfType<T>方法與Cast<T> 方法的區別在於,若是集合中的元素在強制轉換失敗的時候會跳過,而不是拋出異常。code
ToArray 操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲T類型的數組。對象
ToDictionary操做符根據指定的鍵選擇器函數,從IEnumerable<T>建立一個Dictionary<TKey, TValue>。下面的示例中,將查詢到的產品類別集合轉換爲Dictionary<類別ID,類別名稱>的鍵-值集合:blog
var q = db.Categories .ToDictionary ( c => c.CategoryID, c => c.CategoryName ); 生成的sql: SELECT [Extent1].[CategoryID] AS [CategoryID], [Extent1].[CategoryName] AS [CategoryName], [Extent1].[Description] AS [Description], [Extent1].[Picture] AS [Picture] FROM [dbo].[Categories] AS [Extent1] var qq = db.Categories .Select(c => new { c.CategoryID, c.CategoryName }) .ToDictionary(c => c.CategoryID, c => c.CategoryName); 生成的sql: SELECT [Extent1].[CategoryID] AS [CategoryID], [Extent1].[CategoryName] AS [CategoryName] FROM [dbo].[Categories] AS [Extent1]
須要注意的是,若是省略ToDictionary方法的第二個參數(值選擇函數),那麼Value將會保存一個類別對象。還有,若是Key爲null,或者出現重複的Key,都將致使拋出異常。索引
ToList操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲List<T>類型的對象。接口
ToLookup操做符將建立一個 Lookup<TKey, TElement>對象,這是一個one-to-many集合,一個Key能夠對應多個Value。如下的示例以產品表的全部數據做爲數據源,以類別ID做爲Key調用了ToLookup方法,而後遍歷返回的Lookup<TKey, TElement>對象,輸出了類別ID以及此類別下的全部產品名稱:
var q = db.Products .ToLookup ( p => p.CategoryID, p => p.ProductName ); 生成的sql: SELECT [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductName] AS [ProductName], [Extent1].[SupplierID] AS [SupplierID], [Extent1].[CategoryID] AS [CategoryID], [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], [Extent1].[UnitPrice] AS [UnitPrice], [Extent1].[UnitsInStock] AS [UnitsInStock], [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], [Extent1].[ReorderLevel] AS [ReorderLevel], [Extent1].[Discontinued] AS [Discontinued] FROM [dbo].[Products] AS [Extent1]
返回的結果
能夠看出,ToLookup操做與GroupBy操做很類似,只不過GroupBy是延遲加載的,而ToLookup是即時加載。
若是省略第二個參數,則返回產品包裝類。
元素操做符將從一個序列中返回單個指定的元素。
First操做將返回序列中的第一個元素。若是序列中不包含任何元素,則First<T>方法將引起異常。若要在源序列爲空時返回默認值,須要使用FirstOrDefault方法。如下代碼演示了First<T>方法的使用方式:
//無參 var query = db.Employees .First(); 生成的sql: SELECT TOP (1) [c].[EmployeeID] AS [EmployeeID], [c].[LastName] AS [LastName], [c].[FirstName] AS [FirstName], [c].[Title] AS [Title], [c].[TitleOfCourtesy] AS [TitleOfCourtesy], [c].[BirthDate] AS [BirthDate], [c].[HireDate] AS [HireDate], [c].[Address] AS [Address], [c].[City] AS [City], [c].[Region] AS [Region], [c].[PostalCode] AS [PostalCode], [c].[Country] AS [Country], [c].[HomePhone] AS [HomePhone], [c].[Extension] AS [Extension], [c].[Photo] AS [Photo], [c].[Notes] AS [Notes], [c].[ReportsTo] AS [ReportsTo], [c].[PhotoPath] AS [PhotoPath] FROM [dbo].[Employees] AS [c] //有參 var q = db.Employees .First(e => e.FirstName.StartsWith("S")); 生成的sql: SELECT TOP (1) [Extent1].[EmployeeID] AS [EmployeeID], [Extent1].[LastName] AS [LastName], [Extent1].[FirstName] AS [FirstName], [Extent1].[Title] AS [Title], [Extent1].[TitleOfCourtesy] AS [TitleOfCourtesy], [Extent1].[BirthDate] AS [BirthDate], [Extent1].[HireDate] AS [HireDate], [Extent1].[Address] AS [Address], [Extent1].[City] AS [City], [Extent1].[Region] AS [Region], [Extent1].[PostalCode] AS [PostalCode], [Extent1].[Country] AS [Country], [Extent1].[HomePhone] AS [HomePhone], [Extent1].[Extension] AS [Extension], [Extent1].[Photo] AS [Photo], [Extent1].[Notes] AS [Notes], [Extent1].[ReportsTo] AS [ReportsTo], [Extent1].[PhotoPath] AS [PhotoPath] FROM [dbo].[Employees] AS [Extent1] WHERE [Extent1].[FirstName] LIKE N'S%'
上述代碼中使用了First<T>方法的無參方式與有參方式。First<T>的有參方式中能夠指定一個條件,操做將返回序列中知足此條件的第一個元素。從查詢結果上看,source.First<T>(條件)方法與source.Where(條件).First<T>()是同樣的,可是須要注意「First<T>(條件)操做將返回序列中知足此條件的第一個元素」,這將忽略後面的遍歷操做,效率更高。
FirstOrDefault方法將返回序列中的第一個元素;若是序列中不包含任何元素,則返回默認值。它也能夠像First方法同樣傳遞一個條件。須要說明的是若是序列中不包含任何元素,返回的默認值是個怎樣的元素。在這以前,先來看一下FirstOrDefault方法是如何實現的:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } return default(TSource); }
1. 若是調用FirstOrDefault方法的序列爲空,拋出異常
2. 若是序列成功轉換爲List<T>,而且元素數量大於0,則返回首個元素
3. 若是序列沒有成功轉換爲List<T>,則嘗試獲取序列的遍歷器,而後再調用遍歷器的MoveNext方法,若是返回值爲true,則返回當前的元素。
4. 若是上述操做都沒有執行,則使用default(T)關鍵字返回類型T的默認值
如下給出MSDN中,對於default(T)關鍵字的描述:
在泛型類和泛型方法中產生的一個問題是,在預先未知如下狀況時,如何將默認值分配給參數化類型 T:
給定參數化類型 T 的一個變量 t,只有當 T 爲引用類型時,語句 t = null 纔有效;只有當 T 爲數值類型而不是結構時,語句 t = 0 才能正常使用。解決方案是使用default 關鍵字,此關鍵字對於引用類型會返回 null,對於數值類型會返回零。對於結構,此關鍵字將返回初始化爲零或 null 的每一個結構成員,具體取決於這些結構是值類型仍是引用類型。
//var q = // db.Employees // .First(e => e.FirstName.StartsWith("Svvvvvvvvv"));//沒有記錄報錯 var qqq = db.Employees .FirstOrDefault(e => e.FirstName.StartsWith("Svvvvvvvvv"));//沒有記錄爲 null var qq = db.Employees .FirstOrDefault();
Last方法將返回序列中的最後一個元素。使用方法參照First。
LastOrDefault方法將返回序列中的最後一個元素;若是序列中不包含任何元素,則返回默認值。使用方法參照FirstOrDefault。
ElementAt方法返回序列中指定索引處的元素。使用方法參照First。須要注意的是若是索引超出範圍會致使異常。
ElementAtOrDefault方法將返回序列中指定索引處的元素;若是索引超出範圍,則返回默認值。使用方法參照FirstOrDefault。
Single方法的無參形式將從一個序列中返回單個元素,若是該序列包含多個元素,或者沒有元素數爲0,則會引起異常。也就是說,在序列執行Single方法的無參形式時,必須保證該序列有且僅有一個元素。
Single方法的有參形式將從一個序列中返回符合指定條件的惟一元素,若是有多個元素,或者沒有元素符合這一條件,則會引起異常。如下代碼演示了Single的使用方式:
//方法語法 //var q = //報錯:序列包含一個以上的元素 // db.Employees // .Single(); var query = db.Employees .Single(e => e.FirstName.StartsWith("S"));
SingleOrDefault方法的無參形式將從一個序列中返回單個元素。若是元素數爲0,則返回默認值。若是該序列包含多個元素,則會引起異常。
SingleOrDefault方法的有參形式將從一個序列中返回符合指定條件的惟一元素,若是元素數爲0,則返回默認值;若是該序列包含多個元素,則會引起異常。SingleOrDefault的使用方式與Single相同。
須要注意的是,Single方法與SingleOrDefault方法都是即時加載的,在代碼進行到方法所在位置時,若是引起了異常,會馬上拋出。