翻譯
Amro Khasawneh 著 Understanding LINQ (C#)
flankerfc 譯 in MSProject
簡介
本文主要是關注於LINQ——我認為是VS2008(.NET3.5)中最令人興奮的特性。LINQ使查詢成為了.NET中頭等的編程概念,被查詢的資料可以是XML(LINQ to XML)、Databases(LINQ to SQL、LINQ to Dataset、LINQ to Entities)和對象(LINQ to Objects)。LINQ也是可擴充的,允許你建立自訂的LINQ資料提供者(比如:LINQ to Amazon、LINQ to NHibernate、LINQ to LDAP)。
在這裡我會討論C#3.0中的一些新的語言特性和改進,正是它們使得LINQ變得如此強大,讓你可以寫出這樣的代碼:
var result = from c in Customers
where c.City == Boston"
orderby c.LastName descending
select new { c.FirstName, c.LastName, c.Address };
記住,如果你想要使用LINQ,你需要安裝Visual Studio 2008(.NET3.5)。
新的語言特性
I/ 自動屬性
public class Point {
private int _x, _y;
public int X {
get { return _x; }
set { _x = value; }
}
public int Y {
get { return _y; }
set { _y = value; }
}
}
上面的代碼簡單的定義了一個擁有兩個屬性的類。現在,使用VS2008中的C#編譯器,我們就可以用自動屬性寫的更簡單,它能自動的產生帶有get/set操作的私人域。
public class Point {
public int X { get; set; }
public int Y { get; set; }
}
上面的代碼可讀性更好並且簡潔。
(這個特性和LINQ還沒有關係)
II/ 局部變數類型
使用這個特性,聲明一個局部變數,它的具體類型是通過初始設定式來推斷。這點是通過var關鍵詞完成的(這個使用指令碼語言的人應該很熟悉,但它們實際上是有很大區別的)。我們可以寫出如下的代碼:
var num = 50;
var str = "simple string";
var obj = new myType();
var numbers = new int[] {1,2,3};
var dic = new Dictionary<int,myType>();
編譯器會產生IL中間代碼,就如同我們編譯了下面的代碼:
int num = 50;
string str = "simple string";
myType obj = new myType();
int[] numbers = new int[] {1,2,3};
Dictionary<int,myType> dic = new Dictionary<int,myType>();
注意,這裡並不存在無類型的變數,也沒有延遲類型的綁定。編譯器是從右邊的指派陳述式來自動推斷和聲明變數的類型的,var關鍵詞是一個強型別的變數引用。
III/ 對象初始化和集合初始化
我們繼續使用上面的Point類。假設我們想要這個類的一個執行個體,我們會建立對象並設定它的屬性,代碼會是這樣子的:
Point p = new Point();
p.X = 0;
p.Y = 0;
現在,使用對象初始化,就可以像這樣子來重寫:
Point p = new Point() { X = 0, Y = 0 };
這個特性也可以使用在集合上,看看下面這段樣本:
List points = new List {
new Point { X = 2, Y = 5 },
new Point { X = 1, Y = -10 },
new Point { X = 3, Y = 0 }
};
注意,編譯器會產生和上面相等價的長一些的代碼,它依次調用Add()方法來在集合裡添加元素。
IV/ 匿名型別
這個語言特性讓我們可以定義內嵌的類型,而不需要顯式地定義一個類型。換句話說,假設我們沒有定義Point類,卻要使用一個Point對象(即類型是匿名的)。我們可以使用上面提到的對象初始化文法,但不用指定類型名:
var p = new {X = 0, Y = 2};
在VS2008中,你仍然可以使用智能感應。所以如果你繼續使用變數p,就會得到這個匿名型別的屬性列表。
V/ Lambda運算式
在C#2.0中引入了匿名方法,允許在需要委託的地方寫一個代碼塊。匿名方法提供了函數式程式語言的能力,文法顯得很簡潔。Lambda運算式提供了一個更簡潔的文法來寫匿名方法。一個Lambda運算式就是一個參數列表(可以隱式類型),然後是一個=>符號,然後是一個運算式或者一個語句塊。
作為一個樣本,我們定義一個委託類型MyDeleg:
delegate R MyDeleg(A arg);
然後我們就可以使用匿名方法:
MyDeleg<int,bool> IsPositive = delegate(int num) {
return num > 0;
};
我們也可以使用新的Lambda運算式來寫:
MyDeleg<int,bool> IsPositive = num => num > 0;
VI/ 擴充方法
擴充方法可以使你來擴充一個已存在的類型,增加它的方法,而無需繼承它或者重新編譯。所以不像為對象寫助手方法,擴充方法可以直接是對象自己的一部分。
一個樣本,假設我們想要驗證一個string是不是合法的Email地址,我們可以編寫一個方法,輸入為一個string並且返回true或者false。現在,使用擴充方法,我們可以如下這樣做:
public static class MyExtensions {
public static bool IsValidEmailAddress(this string s) {
Regex regex = new Regex( @"^[w-.]+@([w-]+.)+[w-]{2,4}$" );
return regex.IsMatch(s);
}
}
我們定義了一個帶有靜態方法的靜態類。注意,那個靜態方法在參數類型string前面有一個this關鍵詞,這會告訴編譯器這個特殊的擴充方法會增加給string類型的對象。於是我們就可以在string中調用這個成員方法:
using MyExtensions;
string email = Request.QueryString["email"];
if ( email.IsValidEmailAddress() ) {
//
}
值得提出的是,LINQ就是使用了System.Linq命名空間中的擴充方法(比如where()、orderby()、select()、sum()、average()等等),而且它定義了標準查詢操作符,可以用來查詢關聯式資料庫、XML和任何實現了IEnumerable<T>介面的.NET對象。
VII/ 查詢文法
查詢運算式提供了一個語言整合的文法來查詢,它特別像關係和層次查詢語言比如SQL和XQuery。使用LINQ操作符(也就是from...where...select)寫查詢很方便,Visual Studio為它提供了很好的智能感知和編譯時間的檢查支援。
當C#編譯器遇到了一個查詢文法運算式時,實際上它會被轉化為使用擴充方法和Lambda運算式的方法調用。
我們舉一個例子來解釋這個:
var result = from c in Customers
where c.City.StartsWith("B")
orderby c.LastName
select new { c.FirstName, c.LastName, c.Address };
上面的代碼等價於:
var result = Customers.Where( c => c.City.StartsWith("B") )
.OrderBy( c => c.LastName )
.Select( c => new { c.FirstName, c.LastName, c.Address } );
使用查詢文法的好處還有它會使代碼更簡單更易讀。
同時注意,查詢運算式以from開頭,以select或者group結尾。
最後注意
C#3.0中新引入的幾個特性大多數都僅僅是“編譯器技巧”或者“文法糖”,其實編譯器產生的IL中間代碼和原來的是一樣的,這樣他們就獨立於framework和CLR運行時。但是,他們確實需要某些framework的支援,比如System.Core.dll程式集。這就是為什麼擴充方法仍然需要依靠於System.Core.dll裡包含的System.Runtime.CompilerServices.ExtensionAttribute。
另一方面,查詢運算式只是實現了到擴充方法的映射,它包含在System.Linq, System.Data.Linq和System.Xml.Linq命名空間中。
參考和資源
"New Orcas Language Feature" Series by Scott Guthrie: 講解LINQ的系列文章
LINQ Resources: Daniel Moth 寫的日誌
MSDN Magazine (June 2007): 一篇介紹 C# 3.0 和 LINQ 的文章
Visual Studio Orcas Samples: C#/VB LINQ 樣本
Future Versions: C# 3.0
The LINQ Project: LINQ 項目首頁