iT邦幫忙

DAY 21
2

分享一些學習心得系列 第 21

LINQ自學筆記-語法應用-型別篩選-OfType 運算子

篩選資料除了前一篇文章介紹的 Where 運算子,還可以利用型別篩選的方式過濾資料,而且 OfType 運算子是 IEnumerable 擴充方法,可以幫我們把 .Net 1.1/2.0/3.0 時常用的 ArrayList 轉成 IEnumerable<T>,以應用 LINQ 喔。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
篩選資料的另一種方式,就是利用型別篩選運算子,也就是 OfType。以下是它的簽名碼:

public static IEnumerable<TResult> OfType<TResult>(
    this IEnumerable source
)

注意喔,它是 IEnumerable 的擴充方法,和前一篇文章提到的 Where 運算子及其他多數 LINQ 運算子是擴充 IEnumerable<T> 不大一樣。之前在「LINQ自學筆記-打地基-LINQ工作對象」文章有說過,LINQ to Objects 的工作對象必須是 IEnumerable<T>,所以只有實做 IEnumerable 的類別,必須透過 OfType、Case、AsEnumerable 等方法轉成 IEnumerable<T> 才行,因此像 ArrayList 這類在 .Net 3.0 以前常使用,但是在 .Net 3.5 並未實做 IEnumerable<T> 集合類別,就可以透過 OfType<TResult> 擴充方法轉成 IEnumerable<TResult>,讓我們使用 LINQ 對其內容執行查詢作業。

OfType<TResult> 可以幫我們從一個集合中,過濾出指定型別(也就是 TResult)的資料,並指定到一個新的 IEnumerable<TResult> 中,請參閱下述範例:

void Main()
{
    //請注意:ArrayList 並未實做 IEnumerable<T>,所以不能應用 LINQ
    ArrayList ary = new ArrayList();
    //加入三個顧客資料
    ary.Add(new Customer {Id = 1, Name = "Leo", Age = 36});
    ary.Add(new Customer {Id = 2, Name = "Rose", Age = 28});
    ary.Add(new Customer {Id = 3, Name = "Alvin", Age = 2});
    //加入三個顧客的訂單資料
    ary.Add(new Order {CustomerId = 3, OrderDate = new DateTime(2011, 10, 9), Total = 2940});
    ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 10, 10), Total = 3849});
    ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2011, 12, 1), Total = 500});
    ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2012, 2, 28), Total = 1234});
    ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 5, 20), Total = 9520});
    //透過 OfType 過濾出訂單資料,並用 Where 條件查出小於千元的訂單
    var queryOrder = from e in ary.OfType<Order>()
                where e.Total < 1000
                select e;
    foreach (var e in queryOrder)
    {
        Console.WriteLine(e.ToString());
    }
}
//顧客基本資料類別
public class Customer
{
    public int Id {get; set;}
    public string Name {get; set;}
    public int Age { get; set; }
    
    public override string ToString()
    {
        return string.Format("Id = {0}, Name = {1}, Age = {2}", 
                                Id, Name, Age);
    }
}
//訂單基本資料類別
public class Order
{
    public int CustomerId { get; set; }
    public DateTime OrderDate { get; set; }
    public double Total { get; set; }
    
    public override string ToString()
    {
        return string.Format("CustomerId = {0}, OrderDate = {1}, Total = {2}", 
                                CustomerId, OrderDate, Total);
    }
}
//輸出:
//CustomerId = 1, OrderDate = 2011/12/1 上午 12:00:00, Total = 500

上述程式碼先定義了一個 ArrayList,並收容了三個顧客資料和五張訂單資料,接著透過 OfType<Order> 的擴充方法,把 ArrayList 中的訂單資料調出來,並設定 Where 運算子建立篩選條件,只取出訂單金額(Total)小於一千元的訂單,然後指定給新的 IEnumerable<Order>。

我們在 foreach 中可以看到最後的輸出結果是正確,只有一筆小於千元的訂單。
PS. 因為 ArrayList 是使用 Object 型別以儲存各種型態的資料,所以可以同時指派完並不同的型別資料到 ArrayList 中。

使用 OfType 時,請注意以下兩件事:

  1. OfType<TResult> 是屬於延後執行的運算子,所以在 Foreach 之前,我們還可以再更改資料來源的內容。

  2. OfType<TResult> 只會取回符合型別的資料,但這個符合型別的定義,包含可以隱含轉換的型別,例如:類別子系(繼承關係)。

    void Main()
    {
    //請注意:ArrayList 並未實做 IEnumerable,所以不能應用 LINQ
    ArrayList ary = new ArrayList();
    //加入顧客資料
    ary.Add(new Customer {Id = 1, Name = "Leo", Age = 36});
    ary.Add(new Customer {Id = 2, Name = "Rose", Age = 28});
    //加入訂單資料
    ary.Add(new Order {CustomerId = 3, OrderDate = new DateTime(2011, 10, 9), Total = 2940});
    ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 10, 10), Total = 3849});
    ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2011, 12, 1), Total = 500});
    ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2012, 2, 28), Total = 1234});
    ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 5, 20), Total = 9520});
    //透過 OfType 取出 Customer 型別的資料,會同時取出 VIP 的資料
    //請注意,為方便示範我們假設 VIP 的年紀一定是 0。
    var queryCustomer = from e in ary.OfType()
    where e.Age == 0
    select e;
    //建立好 LINQ 查詢表達式後,又對資料來源增加一個 VIP 資料,
    //驗證 OfType 是延後執行的運算子
    ary.Add(new Vip {Id = 3, Name = "Alvin", Age = 0});
    //取回資料並輸出結果,同時證明延後執行和篩選型別的兩個重點
    foreach (var e in queryCustomer)
    {
    Console.WriteLine(e.ToString());
    }
    }
    //顧客基本資料類別
    public class Customer
    {
    public int Id {get; set;}
    public string Name {get; set;}
    public int Age { get; set; }

    public override string ToString()
    {
        return string.Format("Id = {0}, Name = {1}, Age = {2}", 
                                Id, Name, Age);
    }
    

    }
    //VIP 是繼承自 Customer 的類別
    public class Vip:Customer
    {
    public override string ToString()
    {
    return string.Format("Id = {0}, Name = {1}, Age = 問 VIP 年紀是很失禮的事!",
    Id, Name);
    }
    }
    //訂單基本資料類別
    public class Order
    {
    public int CustomerId { get; set; }
    public DateTime OrderDate { get; set; }
    public double Total { get; set; }

    public override string ToString()
    {
        return string.Format("CustomerId = {0}, OrderDate = {1}, Total = {2}", 
                                CustomerId, OrderDate, Total);
    }
    

    }
    //輸出:
    //Id = 3, Name = Alvin, Age = 問 VIP 年紀是很失禮的事!

上述程式碼和第一個範例雷同,差別是我們新增加了一個 Vip 類別繼承自 Customer 類別,並且在建立好 LINQ 查詢表達式後,才把一個 Vip 型別資料加入到資料來源中,最後透過 foreach 取出資料。

因為 OfType 是延後執行的運算子,而且 Vip 類別又是 Customer 的子類別,所以當我們使用 OfType<Customer> 時,最後才加入的 Vip 資料,在 foreach 時順利的被取回顯示出來。


上一篇
LINQ自學筆記-語法應用-篩選資料-Where 運算子
下一篇
LINQ自學筆記-語法應用-資料排序-OrderBy、ThenBy 和遞減
系列文
分享一些學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言