简介
本文主要是关注于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变得如此强大,让你可以写出这样的代码:
记住,如果你想要使用LINQ,你需要安装Visual Studio 2008(.NET3.5)。
新的语言特性
I/ 自动属性
上面的代码简单的定义了一个拥有两个属性的类。现在,使用VS2008中的C#编译器,我们就可以用自动属性写的更简单,它能自动的生成带有get/set操作的私有域。
上面的代码可读性更好并且简洁。
(这个特性和LINQ还没有关系)
II/ 局部变量类型
使用这个特性,声明一个局部变量,它的具体类型是通过初始化表达式来推断。这点是通过var关键词完成的(这个使用脚本语言的人应该很熟悉,但它们实际上是有很大区别的)。我们可以写出如下的代码:
编译器会生成IL中间代码,就如同我们编译了下面的代码:
注意,这里并不存在无类型的变量,也没有推迟类型的绑定。编译器是从右边的赋值语句来自动推断和声明变量的类型的,var关键词是一个强类型的变量引用。
III/ 对象初始化和集合初始化
我们继续使用上面的Point类。假设我们想要这个类的一个实例,我们会建立对象并设置它的属性,代码会是这样子的:
现在,使用对象初始化,就可以像这样子来重写:
本文主要是关注于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 };
新的语言特性
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; } } }
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
>
();
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
>
();
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
} };
IV/ 匿名类型
这个语言特性让我们可以定义内嵌的类型,而不需要显式地定义一个类型。换句话说,假设我们没有定义Point类,却要使用一个Point对象(即类型是匿名的)。我们可以使用上面提到的对象初始化语法,但不用指定类型名:
var p
=
new
{X
=
0
, Y
=
2
};
V/ Lambda表达式
在C#2.0 中引入了匿名方法,允许在需要委托的地方写一个代码块。匿名方法提供了函数式程序语言的能力,语法显得很简洁。Lambda表达式提供了一个更简洁的语法来写匿名方法。一个Lambda表达式就是一个参数列表(可以隐式类型),然后是一个=>符号,然后是一个表达式或者一个语句块。
作为一个示例,我们定义一个委托类型MyDeleg:
delegate
R MyDeleg(A arg);
MyDeleg
<
int
,
bool
>
IsPositive
=
delegate
(
int
num) {
return
num
>
0
; };
MyDeleg
<
int
,
bool
>
IsPositive
=
num
=>
num
>
0
;
扩展方法可以使你来扩展一个已存在的类型,增加它的方法,而无需继承它或者重新编译。所以不像为对象写助手方法,扩展方法可以直接是对象自己的一部分。
一个示例,假设我们想要验证一个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); } }
using
MyExtensions;
string
email
=
Request.QueryString[
"
email
"
];
if
( email.IsValidEmailAddress() ) {
//
}
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命名空间中。