C# 3.0 集合運算 (交集、差集、聯集、互斥) - Rference Type
前篇【C# 3.0 集合運算 (交集、差集、聯集、互斥) - Value Type】介紹實值型別的集合運算,本篇介紹參考型別 (reference type) 的集合運算。首先,繼續拿我常用的 Product 類別,首先建立兩個 List<Product> 集合,當作練習之用。
範例碼,如下:
1: var pList1 = new List<Product>
2: {
3: new Product {ID = 1, Name = "A", Price = 7, ShipDate = new DateTime(2006, 1, 19)},
4: new Product {ID = 2, Name = "B", Price = 237, ShipDate = new DateTime(2006, 10, 17)},
5: new Product {ID = 3, Name = "C", Price = 17, ShipDate = new DateTime(2007, 2, 12)},
6: new Product {ID = 4, Name = "D", Price = 57, ShipDate = new DateTime(2005, 9, 11)}
7: };
8:
9: var pList2 = new List<Product>
10: {
11: new Product {ID = 1, Name = "A", Price = 7, ShipDate = new DateTime(2006, 1, 19)},
12: new Product {ID = 5, Name = "E", Price = 88, ShipDate = new DateTime(2008, 12, 10)},
13: new Product {ID = 6, Name = "F", Price = 99, ShipDate = new DateTime(2008, 6, 4)},
14: new Product {ID = 4, Name = "D", Price = 57, ShipDate = new DateTime(2005, 9, 11)}
15: };
16:
17: // pList1 聯集 pList2
18: foreach (var product in pList1.Union(pList2))
19: {
20: Console.WriteLine(product.ToString());
21: }
輸出的結果為:
1: ID: 1, Name: A, Price: 7, ShipDate: 2006/1/19
2: ID: 2, Name: B, Price: 237, ShipDate: 2006/10/17
3: ID: 3, Name: C, Price: 17, ShipDate: 2007/2/12
4: ID: 4, Name: D, Price: 57, ShipDate: 2005/9/11
5: ID: 1, Name: A, Price: 7, ShipDate: 2006/1/19
6: ID: 5, Name: E, Price: 88, ShipDate: 2008/12/10
7: ID: 6, Name: F, Price: 99, ShipDate: 2008/6/4
8: ID: 4, Name: D, Price: 57, ShipDate: 2005/9/11
這結果似乎有點不太對勁,沒有如我們所預期合併為 Product ID { 1 2 3 4 5 6 } 的集合,而是列出 Product ID { 1 2 3 4 1 5 6 4 } 的組合。這是因為我們缺少了一個動作就是覆寫自訂類別的 Equals() 方法、GetHashCode()方法,使得無法讓編譯器知道什麼是相同的物件。所以我們下一步就是在自定類別 Product 中撰寫 Equals() 方法、GetHashCode()方法。
範例碼,如下:
1: public class Product
2: {
3: public int ID { get; set; }
4: public string Name { get; set; }
5: public double Price { get; set; }
6: public DateTime ShipDate { get; set; }
7:
8: public override string ToString()
9: {
10: return string.Format("ID: {0}, Name: {1}, Price: {2}, ShipDate: {3}", ID, Name, Price, ShipDate.ToShortDateString());
11: }
12:
13: public override bool Equals(object obj)
14: {
15: if(obj is Product)
16: {
17: Product p = (Product) obj;
18: return (p.ID == ID && p.Name == Name && p.Price == Price && p.ShipDate == ShipDate);
19: }
20: return false;
21: }
22:
23: public override int GetHashCode()
24: {
25: return ToString().GetHashCode();
26: }
27: }
此時,我們再一次執行剛剛的程式碼,結果就會變成以下的結果:
1: // pList1 聯集 pList2
2: foreach (var product in pList1.Union(pList2))
3: {
4: Console.WriteLine(product.ToString());
5: }
6:
7: // pList1 聯集 pList2 輸出結果
8: ID: 1, Name: A, Price: 7, ShipDate: 2006/1/19
9: ID: 2, Name: B, Price: 237, ShipDate: 2006/10/17
10: ID: 3, Name: C, Price: 17, ShipDate: 2007/2/12
11: ID: 4, Name: D, Price: 57, ShipDate: 2005/9/11
12: ID: 5, Name: E, Price: 88, ShipDate: 2008/12/10
13: ID: 6, Name: F, Price: 99, ShipDate: 2008/6/4
撰寫了 Equals() 方法、GetHashCode()方法後,剩下來的交集、差集、互斥等集合運算就跟實值型別 (value type) 的使用方式,沒有啥兩樣囉,範例碼如下:
1: // pList1 交集 pList2
2: foreach (var product in pList1.Intersect(pList2))
3: {
4: Console.WriteLine(product.ToString());
5: }
6:
7: // pList1 交集 pList2 輸出結果
8: // ID: 1, Name: A, Price: 7, ShipDate: 2006/1/19
9: // ID: 4, Name: D, Price: 57, ShipDate: 2005/9/11
10:
11: // pList1 差集 pList2
12: foreach (var product in pList1.Except(pList2))
13: {
14: Console.WriteLine(product.ToString());
15: }
16:
17: // pList1 差集 pList2 輸出結果
18: // ID: 2, Name: B, Price: 237, ShipDate: 2006/10/17
19: // ID: 3, Name: C, Price: 17, ShipDate: 2007/2/12
20:
21: // pList1 互斥 pList2
22: foreach (var product in pList1.Except(pList2).Union(pList2.Except(pList1)))
23: {
24: Console.WriteLine(product.ToString());
25: }
26:
27: // pList1 互斥 pList2 輸出結果
28: // ID: 2, Name: B, Price: 237, ShipDate: 2006/10/17
29: // ID: 3, Name: C, Price: 17, ShipDate: 2007/2/12
30: // ID: 5, Name: E, Price: 88, ShipDate: 2008/12/10
31: // ID: 6, Name: F, Price: 99, ShipDate: 2008/6/4