LINQ標準查詢操做符(四) —AsEnumerable,Cast,OfType,ToArray,ToDictionary,ToList,ToLookup,First,Last,ElementAt

10、轉換操做符

轉換操做符是用來實現將輸入對象的類型轉變爲序列的功能。名稱以「As」開頭的轉換方法可更改源集合的靜態類型但不枚舉(延遲加載)此源集合。名稱以「To」開頭的方法可枚舉(即時加載)源集合並將項放入相應的集合類型。sql

1. AsEnumerable

全部實現了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);
}
複製代碼

2. Cast

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

3. OfType

OfType <T> 方法經過提供必要的類型信息,可在IEnumerable(非泛型)的派生對象上調用OfType <T> 方法來得到一個IEnumerable<T>對象。執行OfType<T>方法將返回集合中強制轉換類型成功的全部元素。也就是說,OfType<T>方法與Cast<T> 方法的區別在於,若是集合中的元素在強制轉換失敗的時候會跳過,而不是拋出異常。code

4. ToArray

ToArray 操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲T類型的數組。對象

5. ToDictionary

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,都將致使拋出異常。索引

6. ToList

ToList操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲List<T>類型的對象。接口

7. ToLookup

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是即時加載。

若是省略第二個參數,則返回產品包裝類。

11、元素操做符

元素操做符將從一個序列中返回單個指定的元素。

1. First

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>(條件)操做將返回序列中知足此條件的第一個元素」,這將忽略後面的遍歷操做,效率更高。

2. FirstOrDefault

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,只有當 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();
複製代碼

 

3. Last

Last方法將返回序列中的最後一個元素。使用方法參照First。

4. LastOrDefault

LastOrDefault方法將返回序列中的最後一個元素;若是序列中不包含任何元素,則返回默認值。使用方法參照FirstOrDefault。

5. ElementAt

ElementAt方法返回序列中指定索引處的元素。使用方法參照First。須要注意的是若是索引超出範圍會致使異常。

6. ElementAtOrDefault

ElementAtOrDefault方法將返回序列中指定索引處的元素;若是索引超出範圍,則返回默認值。使用方法參照FirstOrDefault。

7. Single

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方法都是即時加載的,在代碼進行到方法所在位置時,若是引起了異常,會馬上拋出。

相關文章
相關標籤/搜索