了解如何取出資料後,接著要介紹最常用的條件篩選運算子:Where。另外本文會提到一個重點,就是 LINQ to Entity 和 LINQ to SQL 通常不支援那種類型的標準查詢運算子之多載方法。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
要從大量的資料中,取得我們需要的內容,最常用且基本的方式就是用 Where 運算子來達成:
//資料來源
string[] words = { "Hello", "wonderful", "LINQ", "beautiful", "World" };
//LINQ查詢表達式
var shortWords =
from word in words
where word.Length <= 5
select word;
//上述查詢表達式對等是方法架構查詢語法
//var query = words.Where(w => w.Length <= 5);
//顯示查詢結果
foreach (var e in shortWords)
{
Console.Write(e.ToString() + " ");
}
//輸出:Hello LINQ World
上述程式碼設定了一個字串陣列做為資料來源,然後用 LINQ 過濾出字串長度小於等於 5 的項目,便可取得「Hello LINQ World」的輸出結果。
以下為 Where 運算子的多載方法:
以上兩個多載,個人實務上沒用過第二個,原因除了在篩選資料時,比較少用到索引值之外,另一個重要的原因是,LINQ to Entity 和 LINQ to SQL 不支援第二個多載版本!
請注意,許多 LINQ 標準查詢運算子都有一個可接受整數引數的多載版本。此整數引數會「通常」對應到所操作之序列(Sequence)中以零為起始的索引,在 LINQ to Entity 和 LINQ to SQL 中,「通常」不支援 LINQ 標準查詢運算子的這些多載版本,若嘗試使用,在執行時期會擲回 NotSupportedException 。
以下為 LINQ to Entity 使用第二個多載方法時,會發生的執行時期錯誤:
以下為 LINQ to SQL 使用第二個多載方法時,會發生的執行時期錯誤:
上述說明中,我用「通常」字眼,是因為怕大家以為所有會帶入 int 型別的多載都不支援,這並不正確,因為帶入 int 型別參數,除了索引值外,也有可能是數量,例如 Take 方法就要傳入一個 int 值表達要取前多少筆資料。實際上我在做教育訓練時,的確會有學員誤以為帶 int 型別的參數就只能在 LINQ to Objects 中使用,所以我還是要補充說明一下。
最後再總結一下 Where 運算子:它最重要的就是後面的條件判斷式。我們可以使用標準 C# 運算子(大於、小於、等於……)做簡單判斷,也可以用一個方法做成複雜的判斷式,然後透過回傳 bool 來通知 Where 運算子判斷結果;Where 運算子也同時支援查詢語法(Query syntax)和方法語法(Method syntax),因此我們可以用 Lambda 運算式來帶入條件判斷邏輯。
本篇最後,用下面這個範例讓大家了解上述幾種應用:
void Main()
{
int[] numbers = { 0, 55, 30, 20, 15, 90, 85, 40, 75, 5 };
var query = from n in numbers
where n > 30 && n < 90
select n;
query.Dump("Standard query syntax");
//複雜的判斷邏輯,可以呼叫外部方法協助處理,只要會回傳bool就行了
var queryFibonacci = from n in numbers
where IsFibonacciNumber(n)
select n;
queryFibonacci.Dump("Is Fibonacci number");
//當然,也可以用方法架構查詢,搭配 Lambda 運算式來處理簡單邏輯判斷
var queryOdd = numbers.Where(n => n % 2 != 0);
queryOdd.Dump("Get odd number");
}
public bool IsFibonacciNumber(int number)
{
double f1 = 5 * Math.Pow(number, 2) + 4;
double f2 = 5 * Math.Pow(number, 2) - 4;
var sqrtF1 = (long)Math.Sqrt(f1);
var sqrtF2 = (long)Math.Sqrt(f2);
var squareF1 = Math.Pow(sqrtF1, 2);
var squareF2 = Math.Pow(sqrtF2, 2);
bool isFibonacci = (squareF1 == f1) || (squareF2 == f2);
return isFibonacci;
}