本文將為大家介紹 LINQ 設定方法中,Any、All、Contains 這三個標準查詢運算子。這三個運算子可以讓我們判斷資料是否存在或包含特定的項目。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
Any 運算子用來判斷序列中,是否至少有一個項目,或者是否有任何項目符合指定的條件。以下是兩個多載方法的簽名碼:
public static bool Any<TSource>(
this IEnumerable<TSource> source
)
public static bool Any<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
第一個多載方法,不用傳入任何參數,可直接在來源序列上調用 Any 運算子,只要序列不是空的(至少有一個項目),就會回傳 true,反之回傳 false。請注意,若來源序列是 null,則執行時會拋回 ArgumentNullException 例外:
var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba","IBM","HP","Dell"};
var emptyList = new List<string>() {};
Console.WriteLine(list.Any());
Console.WriteLine(emptyList.Any());
/* 輸出:
True
False
*/
上述程式碼定義了兩個字串清單做資料來源,list 清單有 7 個項目,emptyList 沒有任何項目,分別在兩個序列上調用 Any 運算子的第一個多載方法,得到輸出結果。
第二個多載方法則是可以讓我們傳入一個委派 predicate,自行決定序列中的項目要符合什麼樣的邏輯才為真(true):
var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba","IBM","HP","Dell"};
Console.WriteLine(list.Any (l => l == "Acer"));
Console.WriteLine(list.Any (l => l.Length > 10));
/* 輸出:
True
False
*/
上述程式碼定義一個廠牌名稱的資料來源清單,然後用 Any 運算子第二個多載方法,定義兩個 LINQ 查詢。第一個查詢檢查序列中是否有 Acer 這個廠牌名稱,第二個查詢檢查序列中項目,是否有任何一個長度大於 10。
第二個要說明的主題是 All 運算子,用途是判斷來源序列中【所有】項目是否都符合設定的條件,所以它只有一個方法簽名:
public static bool All<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
All 運算子要求我們必須傳入一個委派 predicate,自行設定條件,然後來源序列中所有項目都必須符合才為真(true)。
我們可以比較一下和 Any 運算子的差異:Any 運算子第二個多載方法是判斷來源序列中是否有【任一個】項目符合指定條件,All 運算子則是判斷來源序列【所有】項目都必須符合條件才回傳 true。以下為 All 運算子的範例:
var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba", "Dell"};
Console.WriteLine(list.All(l => l.Length > 3));
Console.WriteLine(list.All(l => l.Contains("A")));
/* 輸出:
True
False
*/
上述程式碼建立一個字串清單,再定義兩個 LINQ 查詢,第一個是設定所有項目的長度是否都大於 3,第二個查詢則是檢查序列中所有項目是否都有包含 "A",並輸出兩個查詢的查詢結果。
All 運算子有一個特性請大家特別注意,如果來源序列是空的(沒有任何項目),則不管設定的條件為何,回傳都是真(true):
var emptyList = new List<string>() {};
Console.WriteLine(emptyList.All(l => l.Contains("A")));
/* 輸出:
True
*/
但是來源序列若是 null,則會造成 ArgumentNullException 例外。
本文最後一個主題是 Contains 運算子,它用來判斷序列中是否包含指定的項目。請注意,.Net 3.5 的 LINQ to Entities、LINQ to SQL 完全不支援 Contains 運算子!但是在 .Net 4.0 版本,則支援第一個多載方法。以下是 Contains 運算子兩個多載方法:
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value
)
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value,
IEqualityComparer<TSource> comparer
)
兩個多載方法差別就是第一個方法使用型別(TSource)預設的相等比較子,第二個方法則是傳入自訂的 IEqualityComparer<TSource> 來判斷序列中是否包含指定項目:
var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba", "Dell"};
Console.WriteLine(list.Contains("Acer"));
Console.WriteLine(list.Contains("A"));
/* 輸出:
True
False
*/
上述程式碼建立一個字串清單,再定義兩個 LINQ 查詢,第一個查詢用 Contains 運算子檢查序列中是否包含 "Acer" 這個字串,第二個查詢則是檢查是否包含 "A" 字串。第二個查詢主要是要突顯這裡的 IEnumerable<TSource>.Contains 運算子檢查是兩個物件必須完全相同,和 String.Contains 方法是不同的意思。是的,我在學習 LINQ 時,曾一度混淆過,所以特別 Highlight 一下。
Contains 運算子第二個多載方法可以讓我們自訂物件相等的判斷邏輯:
void Main()
{
var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba", "Dell"};
var comparer = new IgnoreCaseSensitive();
Console.WriteLine(list.Contains("asus"));
Console.WriteLine(list.Contains("asus", comparer));
}
//自訂忽略大小寫的相等比較子
public class IgnoreCaseSensitive : IEqualityComparer<string>
{
public bool Equals(string s1, string s2)
{
return (s1.ToUpper().Equals(s2.ToUpper()));
}
public int GetHashCode(string str)
{
return str.ToUpper().GetHashCode();
}
}
/* 輸出:
False
True
*/
上述程式碼,我們建立一個字串清單,也建立一個實做 IEqualityComparer<string> 的類別,用以判斷兩個字串是否相等時,要忽略大小寫(程式邏輯是全部轉大寫再呼叫字串的 Equals 方法),然後設定兩個 LINQ 查詢,第一個查詢使用 Contain 運算子第一個多載方法,所以會使用字串的預設相等比較子,第二個查詢使用自訂的 IgnoreCaseSensitive 類別來做忽略大小寫的字串比較,然後輸出結果。
文章結束前,提醒大家,這三個運算子都是立即執行查詢的運算子,使用時請注意一下。