本文說明 LINQ 是如何實現的,知道這點,才能更好地使用它~
如果你剛接觸 LINQ,那么可能不會那么快就理解。因為,LINQ 涉及的技術比較多,可以說它是眾多技術的集大成者,因此使用 LINQ 需要你對幾個知識點有一定認識,包括泛型委托、擴展方法、匿名函數、λ 表達式(Lambda 表達式),在此基礎上,才可能靈活運用 LINQ。這也是為什么 LINQ 在 VS 2008 才出現,而不是之前。
最好是,先學習泛型委托,再通過學習 System.Array 類提高對匿名函數和 λ 表達式的理解。
如果有一個關于一級方程式賽車冠軍 Racer 的集合 ,Racer 包括賽車手的姓名、國家、冠軍次數等屬性,要想對這個集合操作,比如檢索、排序等等,在 C# 3.0 以前,我們要想知道 Brazil 這個國家的冠軍都有誰,并且按降序排序,代碼如下所示:
IEnumerable<Racer> brazilChampions = champions.Where(
delegate(Racer r)
{
return r.Country == "Brazil";
}).OrderByDescending(
delegate(Racer r)
{
return r.Wins;
}).Select(
delegate(Racer r)
{
return r;
});
其中,champions 是冠軍集合。
注意:where、OrderByDescending、Select 函數都把委托作為函數參數,鏈式調用最后會返回一個可用 foreach 語句迭代的 IEnumerable 可枚舉對象 brazilChampions。
雖然上面代碼看上去還可以,但是,有 delegate 關鍵字、有形式參數,還不夠簡潔。是否可以沒有這些啰嗦的東西呢?估計你也想到了——λ 表達式!代碼如下所示:
IEnumerable<Racer> brazilChampions = champions.
Where(r => r.Country == "Brazil").
OrderByDescending(r => r.Wins).
Select(r => r);
嗯,簡潔多了~可我們又想了,既然是查詢,那能不能像數據庫查詢那樣寫個 SQL 語句呢?比如:select * from champions t where t.country="Brazil" orderby desc 或類似形式,SQL 中有謂詞~
當然有,這就是 LINQ~
但 LINQ 不是一下冒出來的,它有個過程,這也是為什么 LINQ 在 VS 2008 才出現,而不是之前。
了解如何實現 LINQ,才能更好地使用它~
要想了解 LINQ 是如何實現的,需要知道兩個必要的技術點:擴展方法和 λ 表達式。當然,還涉及到泛型委托。
其實,擴展方法并不陌生,我第一次注意這個用法是在 Ext.Net,它的項目里有個工具類程序集。其中的字符串工具類 StringUtils ,如判斷字符串是否為“空”,無論是 null,還是長度為 0 都返回 true,從而擴展 .Net 的字符串操作。如下所示 StringUtils 工具類的代碼片段:
public static class StringUtils
{
/// <summary>
/// Determine is the string is null or empty.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static bool IsEmpty(this string text)
{
return string.IsNullOrEmpty(text);
}
/// <summary>
/// Determine is the string is NOT null or empty.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static bool IsNotEmpty(this string text)
{
return !text.IsEmpty();
}
……
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <param name="pattern"></param>
/// <returns></returns>
public static bool Test(this string text, string pattern)
{
return Regex.IsMatch(text, pattern);
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <param name="pattern"></param>
/// <param name="options"></param>
/// <returns></returns>
public static bool Test(this string text, string pattern, RegexOptions options)
{
return Regex.IsMatch(text, pattern, options);
}
……
}
顯然,.Net 沒有對字符串提供 IsEmpty 和 Test 方法(其實,.Net 提供了 string.IsNullOrEmpty 方法),但如果在你的程序集中定義了這個類,那么就能在你的代碼中對字符串用“.”直接調用這些方法,如 "helloworld".IsEmpty()。
注意,StringUtils 是靜態類。靜態類主要是為了共享,靜態類的成員也必須是靜態的。靜態類一般在程序加載的時候被構造。靜態類可以有構造函數,但只被調用一次。這樣,在你的代碼中就不需要實例化這個類,更重要的是,對函數參數 string 類型使用了 this 關鍵字。
因為,所有的泛型集合都繼承了 IEnumerable<T> 接口,這樣,我們自定義過濾函數 Where,對 IEnumerable<T> 用擴展方法。如下所示,泛型集合根據指定條件返回符合條件的集合:
public static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> PRedicate)
{
foreach (T item in source)
{
if (predicate(item))
yield return item;
}
}
其中,函數 Where 有兩個參數:一個是帶 this 的 IEnumerable<T>,另一個是 .net 提供的只有兩個輸入參數的帶返回值的泛型委托 Func<T, TResult>。這樣,對任何泛型集合調用 Where 方法都相當于:
champions.Where(Func<T, bool> predicate)
如:
IEnumerable<Racer> brazilChampions = champions.Where(
delegate(Racer r)
{
return r.Country == "Brazil";
});
既然自定義的泛型集合 Where 方法返回了 IEnumerable<T> 類型,我們當然可以再定義一個對泛型集合的排序函數,比如叫 OrderByDescending(this IEnumerable<T> source, Func<T, int> predicate)。
因為自定義函數 Where 的返回類型,也是函數 OrderByDescending 的輸入類型,所以它們能形成一個調用鏈。
C# 3.0 引入了一個新的語法——λ 表達式(Lambda 表達式)。λ 演算背后是有數學基礎的,也就是說,函數聲明,包括函數名、形參、返回類型等等都是可以沒有的,直接函數體。在對泛型集合調用上面自定義的 Where 函數時,就可以這樣寫:
IEnumerable<Racer> brazilChampions = champions.Where(r => r.Country == "Brazil");
其中,r 并沒有定義。這下代碼,簡潔多了。
前面說了擴展方法和 λ 表達式,現在離 LINQ 近了。
我們希望 LINQ 跟 SQL 一樣,也有類 select、where、group by、order by 這些關鍵字,編譯器只要把這些關鍵字映射到相應的擴展方法上就行了。
而 LINQ 的關鍵字跟 SQL 很像,比如 from、where、orderby、descending 和 select 等。
上面的泛型查詢,改成 LINQ 如下所示:
var query = from r in champions
where r.Country == "Brazil"
orderby r.Wins descending
select r;
其中,
- 1,
where r.Country == "Brazil"轉換成
Where(r=>r.country=="Brazil");
2,orderby r.Wins descending轉換成
OrderByDescending(r=>r.Wins)
- 3,
select r轉換成
Select()
LINQ 查詢必須以 from 子句開始,以 select 或 group 子句結束。在這兩個子句之間,可以使用 where、orderby、jion 和其他 from 子句。
LINQ 的具體用法你就得自己研究了,不在本文討論范圍內~
下載 Demo
新聞熱點
疑難解答