本文例子大部分来自于(Apress pro linq)
为了使LINQ可以无缝的和C#语言整合在一起,微软对C#3.0加入了些新功能,这里主要介绍和LINQ相关的加强。
1、var关键字,集合初始化以及匿名类型
2、Lambda 表达式
3、部分(Partial )方法
4、扩展方法
5、表达式树
1、var关键字,集合初始化以及匿名类型
var:
可以赋予局部变量推断“类型”var 而不是显式类型。var 关键字指示编译器根据初始化语句右侧的表达式推断变量的类型。推断类型可以是内置类型、匿名类型、用户定义类型、.NET Framework 类库中定义的类型或任何表达式。var只是表示由编译器确定和分配最适当的类型。和javascript中的var的概念不同.例如:var i = 5;编译完之后i就是一个整型,和int i=5;没有任何区别.在很多情况下,var 是可选的,它只是提供了语法上的便利。
请看如下例子:
var i = 5;//int
var j = 23.56;//double
var k = "C Sharp";//string
var x;//错误
var y = null;//错误
var z = { 1, 2, 3 };//错误
对象和集合初始值设定项:
User user = new User();
user.Id = 1;
user.Name = “lfm";
user.Age = 22;
在VS2008中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。我们可以这样简化:像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。
User user = new User { Id = 1, Name = “lfm", Age = 22 };
又例如,我把二个人加到一个基于泛型的类型为User的List集合中:
List<User> user = new List<User>{
new User{Id=1,Name=“lfm",Age=22},
new User{Id=2,Name=“tmx",Age=25},
};
匿名类型:
匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。类型名由编译器生成,并且不能在源代码级使用。这些属性的类型由编译器推断。
匿名类型允许定义行内类型,无须显式定义类型。在将匿名类型分配给变量时,必须使用 var 构造初始化该变量。
//属性也不需要申明,这些属性的类型由编译器推断
var p1 = new { Id = 1, Name = “lfm", Age = 22 };
var p2 = new { Id = 2, Name = “tmx", Age = 25 };
p1 = p2;//p1,p2结构相同,可以互相赋值
在这里编译器会认为p1,p2相当于:
public class SomeType
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
备注(from msdn)
匿名类型是直接从对象派生的引用类型。尽管应用程序无法访问匿名类型,但编译器仍会为其提供一个名称。从公共语言运行库的角度来看,匿名类型与任何其他引用类型没有什么不同。
如果两个或更多个匿名类型以相同的顺序具有相同数量和种类的属性,则编译器会将这些匿名类型视为相同的类型,并且它们共享编译器生成的相同类型信息。
匿名类型具有方法范围。若要向方法边界外部传递一个匿名类型或一个包含匿名类型的集合,必须首先将匿名类型强制转换为对象。但是,这会使匿名类型的强类型化无效。如果必须存储查询结果或者必须将查询结果传递到方法边界外部,请考虑使用普通的命名结构或类而不是匿名类型。
2、Lambda 表达式
在c#3.0中微软加入了“Lambda 表达式”,“Lambda 表达式”的概念最早是数学家Alonzo Church在1936年提出的,早在LISP语言中就已经得到应用。这些表达式提供了简短的语法来表达一个运算法则。在C#3.0中“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。在我们详细讲解“Lambda 表达式”之前,我们先来了解一下“Lambda 表达式”出现的原因。
我们假设这样一个场景,我们希望有这样一个函数,对一个整型数组进行过滤,而过滤得条件在编写函数时还不知道,直到使用这个函数的时候可以根据当前的情况编写过滤条件函数,大家一看到这个场景可能马上想到,可以使用委托阿。ok,代码如下:
{
public delegate bool IntFilter(int i);
public static int[] FilterArrayOfInts(int[] ints, IntFilter filter)
{
ArrayList aList = new ArrayList();
foreach (int i in ints)
{
if (filter(i))
{
aList.Add(i);
}
}
return ((int[])aList.ToArray(typeof(int)));
}
}
{
public static bool IsOdd(int i)
{
return ((i & 1) == 1);
}
}
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums = Common.FilterArrayOfInts(nums, Application.IsOdd);
foreach (int i in oddNums)
Console.WriteLine(i);
}
结果为:
1
3
5
7
9
看了这段代码之后,大家可能会觉得这个实现可能不够好,这里的IsOdd函数可能只用这么一次,并且也太简单了,不值得当成一个函数来用,于是我们想到了C#2.0的匿名委托,实现代码如下:
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums =
Common.FilterArrayOfInts(nums, delegate(int i) { return ((i & 1) == 1); });
foreach (int i in oddNums)
Console.WriteLine(i);
}
恩,这段代码似乎相当棒了,解决了我刚才说的所有问题,但是看起来好象不够简洁,也比较难懂些。我希望有一个更好的方式来解决这个问题。
我们先来看看“Lambda 表达式”的用法,所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。Lambda 表达式 x => x * x 读作“x goes to x 乘x”其意义就是输入x返回x乘x。
Lambda 表达式返回表达式的结果,并采用以下基本形式:
(input parameters) => {statement;}
多个参数时可以使用如下方式
(param1, param2, …paramN) => expr
更为复杂的方式可以如下表示
(param1, param2, …paramN) =>
{
statement1;
statement2;
…
statementN;
return(lambda_expression_return_type);
}
这里大家需要知道的是“Lambda 表达式”其实就是一个匿名委托,可以使用“Lambda 表达式”的地方必须是可以使用委托的地方,Lambda 运算符 =>左边的是输入参数,Lambda 运算符 =>右边的部分是执行后的输出结果
比如
x => x.Length > 0
相当于
delegate(string x) { return x.Length > 0; }
大家理解了“Lambda 表达式”,我们就可以使用“Lambda 表达式”来完成刚才过滤数据的场景了。代码如下:
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums = Common.FilterArrayOfInts(nums, i => ((i & 1) == 1));
foreach (int i in oddNums)
Console.WriteLine(i);
}
这段代码比刚才匿名委托的方式易读性要好很多了吧