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變得如此強大~~
翻譯
Amro Khasawneh
著 Understanding LINQ (C#)
flanker譯(還請指教啊)
簡介
本 文主要是關注於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
命名空間中。
參考和資源