建议大家看完Linq查询后再看各个语法新特性对Linq的意义,这样会更加有帮助一些。
1、自动属性。
class Class
{
//C#2.0 属性
//private int _id;
//public int ID
//{
// get {
// return _id;
// }
// set
// {
// _id = value;
// }
//}
//C#3.0 属性 可以给get set加访问修饰符
public int ID { get; private set; }
public string Name { get; set; }
public Class(int id)
{
//加了private之后的属性只能在类的内部访问
this.ID = id;
}
}
本质:和原来的属性没啥两样 ,简化了语法而已。
对Linq的意义:无。
2、初始化器。
private static void Initializer()
{
//C#2.0 对象初始化
//Class c = new Class(1);
//c.Name = "终极一班";
//C#3.0 对象初始化器
Class c = new Class(1) { Name = "终极一班" };
//C#2.0 集合初始化
//ClassCollection list = new ClassCollection();
//list.Add(c);
//C#3.0 集合初始化器
ClassCollection list = new ClassCollection
{
new Class(1) { Name="终极一班"},
new Class(2){Name="终极二班"}
};
foreach (Class item in list)
{
Console.WriteLine(item.ID + " " + item.Name);
}
}
相关的班级集合类代码:
class ClassCollection : List <Class>
{ }
本质:和原来的构造函数初始化或构造后通过属性初始化没啥两样 ,简化了语法而已。
对Linq的意义:和匿名类型结合起来构造查询结果集合里面的新元素类型。
4、具有隐式类型的局部变量
private static void Var()
{
var i = 1;// 编译过后的结果实际是 int i=1; var并不是动态变量,它的类型实际上是c#编译器通过上下文推断是int
//var i = DateTime.Now; //编译不过,和JavaScript不一样
var d = DateTime.Now;//=后面支持各种类型
var a = new int[] { 1, 2, 3 };//var也支持数组
foreach (var item in a)//item的类型通过C#编译器推断得知是int
{
Console.WriteLine(i);
}
//var x; // 错误,没有用来推断类型的初始化器
//var y = { 1, 2, 3 }; // 错误,不允许使用集合初始化器
//var z = null; // 错误,不允许出现空类型
}
本质:var并非动态类型 ,C#仍然是静态语言,引入var方便我们写代码了,可以不管“=”后面的赋值表达式类型了,由编译器自己去推断生成对应类型了。
对Linq的意义:可以自动推断出Linq查询返回的集合类型。
5、匿名类型。
private static void AnonymousType()
{
var v = new { Name = "张三", Sex = true };//无须显示声明一个类,而且在初始化器里面可以获取上下文的变量——闭包
Console.WriteLine(v.Name);
}
本质:有了匿名类型后我们不需要显示的声明一个类型了,这个类型由C#编译器自动生成,而且利用了初始化器和var的新特性
对Linq的意义:和初始化器结合起来构造查询结果集合里面的新元素类型。
6、扩展方法。
比如我们现在想给int类型增加(扩展)一个方法,判断一个整数自身是否偶数,我们期望的语法是这样的:
private static void ExtendMethod()
{
int i = 2;
Console.WriteLine(i.IsEven());
}
注意原来int原来是没有IsEven()这个方法的,要实现这个方法,必须写一个静态类和一个静态方法。
static class MyExtention
{
public static bool IsEven(this int num)//this 表示针对int的实例和索引器的this的含义是一样的,int表示给int这种类型进行扩展
{
return num % 2 == 0;
}
}
本质:编译i.IsEven()的本质是C#编译器生成了了MyExtention.IsEven(i)的代码,实际上仍然没有破坏类型的结构,并不是真的象语法那样平白无故给int增加了一个IsEven()方法,和设计模式里面的Visitor模式动态注入方法还是有区别的。
对Linq的意义:用来对集合类型扩展不同的查询方法。
7、Lambda表达式和Linq查询。
接下来我们通过一个例子来看一下Lambda表达式和Linq查询的关系:我们现在想给ClassCollection增加一个过滤方法,方法的目的是能够过滤返回班级名称为“终极一班”的集合来。
0)首先给MyExtention增加这么一个静态方法:
public static ClassCollection Filter(this ClassCollection classes)
{
var newlist = new ClassCollection();
foreach (var item in classes)
{
if (item.Name=="终极一班")
{
newlist.Add(item);
}
}
return newlist;
}
private static void LambdaLinq()
{
var classes = GetClasses();
//var students = GetStudents();
//0 原始版本
var result = classes.Filter();
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}
}
相关的工厂方法:
static ClassCollection GetClasses()
{
return new ClassCollection{
new Class(1){ Name = "终极一班"},
new Class(2){ Name = "终极二班"},
};
}
1)现在需求发生了变化,需要上面的红色部分需要发生变化,也就是说我们希望这个查询条件可以在我们调用Filter方法的时候动态的指定,这时候我们可以把这个变化封装成一个接口,当然还可以封装成一个委托,这是.net的非常好用的独有特性,委托的最直接的作用可以把一个具体的方法引用封装成一个变量传递。好,开始变形!
delegate bool FilterHandler(Class c); //注意这个要放到namespace下面,不要放到Program类里面
public static ClassCollection Filter(this ClassCollection classes,FilterHandler f)
{
var newlist = new ClassCollection();
foreach (var item in classes)
{
if (f(item))
{
newlist.Add(item);
}
}
return newlist;
}
static bool F(Class c)
{
return c.Name == "终极一班";
}
private static void LambdaLinq()
{
var classes = GetClasses();
// C#1.0 使用委托封装过滤条件
FilterHandler f=new FilterHandler(F);
var result = classes.Filter(f);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}
}
我们声明了一个委托FilterHandler,只要满足这个委托的方法我们都可以传递给Filter方法,这样就实现了动态的改变查询条件的目的,F方法内部可以是任意的查询条件比如return c.Name != "终极一班";同时我们不需要改变Filter方法内部稳定的部分。
2)c#2.0里面也支持直接把一个方法传给一个委托,但本质上也是编译器把方法转换成了一个委托,例如上面:
private static void LambdaLinq()
{
var classes = GetClasses();
// C#2.0 直接传递方法
var result = classes.Filter(F);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}
}
3)C#2.0里面有个新特性,叫匿名方法,我们可以直接传递匿名方法:
private static void LambdaLinq()
{
var classes = GetClasses();
// C#2.0 传递匿名方法
var result = classes.Filter(delegate(Class c) { return c.Name == "终极一班"; });
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}
}
好,变形到这里,我们发现这个匿名其实不仅仅可以给我们带来不用给方法命名的好处,在这个方法内部我们还可以使用外部上下文环境的变量成员,这个特性也叫“闭包(Closure)”,JavaScript也支持这个特性,比如:
private static void LambdaLinq()
{
var classes = GetClasses();
string className = "终极一班";
// C#2.0 传递匿名方法
var result = classes.Filter(delegate(Class c) { return c.Name == className; });
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + item.Name);
}
}