c#的时间类型是一个类,引用的时候直接声明构造函数
var inchicago = from d in doctors where d.City == "Chicago" select d;
这里的d 不用额外声明
这里是选择城市是chicago的
我们还可以排序
var byname = from d in doctors
where d.City == "Chicago"
orderby d.FamilyLastName
select d;
根据FamilyLastName排序
这里是统计分组
var bycity = from d in doctors
group d by d.City into g
//g是一个接口可以得到一个返回值,就是group中相同的元素,而且这个接口只能在group前后
orderby g.Key
select new { City = g.Key, Count = g.Count() }; // 自动生成一个引用,里面有两个属性city和count,并赋值
如果我们想有选择的把城市在chicago的员工添加在一个List里面,.net2.0给我们提供了委托的解决方案
public delegate bool Predicate<T>(T obj); // 这是在.net2.0里面定义好的,我们要做到就是写一个限制函数
public bool IsInChicago(Doctor obj) // 选出城市在chicago的
{
return obj.City == "Chicago";
}
这个放在doctor类里面
List<Doctor> inchicago = doctors.FindAll(new Predicate<Doctor>(this.IsInChicago));
//添加IsInChicago这个函数到predicate这个委托中
//FindAll传递的参数是判断函数,就是返回bool的函数,并且传入参数是一个的
我们还可以利用匿名参数让代码更具可读性
List<Doctor> inchicago = doctors.FindAll(delegate(Doctor d)
{
return d.City == "Chicago";
} );
Lambda表达式
从开发者的角度来说Lambda表达式一个相当简洁的匿名方法,Lambda表达式需要一个传入参数,和赋值的表达式,不需要命名。书写规范如下
x=>f(x)
所以上面的判断我们就可以用lambda表达式来写
List<Doctor> inchicago = doctors.FindAll(d => d.City == "Chicago");
Lambda语法很简洁去掉了额外的代码,例如{},他相当于一个匿名的方法,在运行的时候返回值,实际上编译器把它翻译成托管的代码,特别是当他做为匿名的方法的时候
有趣的是,Lambda表达式和匿名方法有着微妙的差别,后者需要参数的类型,而前者不要
d => d.City == "Chicago" //我们不知道d的类型
delegate(?????? d) // 这里需要 d的类型
{
return d.City == "Chicago";
}
实际上c#3.0是根据上下文来推断Lambda的参数类型,类型推断使LInq更加方便,并且没有降低安全性和执行效率。
我们可以声明var 来利用类型推断这个功能,另外一下声明格式是不允许的
var obj2; // ERROR: 必须赋值
var obj3 = null; // ERROR: 不能为null
但如果推断类型成功,那么这个数据的类型将在继续的编译中永久不变,错误的引用或声明都将报错。
我们不要把var和vb中的variant,或者是javascript中的var混淆,在这些语言中,var的类型是可以变化的,所以类型的检查就是在运行的时候,而且会引发安全问题,而c#3.0类型是不能更改的,
var obj4 = "hi!";
obj4.Close(); //(ERROR: 'string' does not contain 'Close').string没有close这个函数
object obj5 = "hi"; // 虽然是字符串,但是类型是object
var obj6 = obj5; // 引用5 但是还是object
string s1 = obj6.ToUpper(); // ERROR: 'object' does not contain 'ToUpper',因为不是字符串所以没有ToUpper函数
也许开发者认为类型检查功能用处不是很大,但是,这个是为了Linq专门设计的,因为查询返回的类型是复杂的,所以他在linq中取得了显著的成效,如果没有他,那么开发者需要知道返回的类型,现实中这有时不太可能
匿名类型和元素初始化表达式
我们看下面一个例子
var query = from d in doctors
where d.City == "Chicago"
select new { d.GivenFirstName, d.FamilyLastName };
这里的 new关键字没有加()其实这是初始化一个对象,但是这个类是一个c#3.0的匿名类型,编译器会自动地给他一个唯一的名字,并且自动根据{}里面的内容自动设置类里面的参数和访问器。
new { d.GivenFirstName, d.FamilyLastName }中的访问器是GivenFirstName,FamilyLastName
还有Tostring Equals GetHashCode三个函数
如果你想让属性以特殊的名字初始化你也可以这样
new { First = d.GivenFirstName, Last = d.FamilyLastName }
访问器就成了First Last
匿名的类型添加到C#3.0里面就是为了支持linq,为了允许用户在一个熟悉的面向对象的数据包里面使用。并加上类型推测,这样我们就可以更安全的使用类型检测属性了。
目前限制匿名类型是,他们无法存取以外的界定类的名称,您会使用指定类?在理论上你可以编译代码在一个单独的集合,然后使用所产生的名称,但是这是一个明显的不良做法(目前阻止的事实是,即所产生的名称在源代码级是无效的)。问题是,设计N层应用,其层次沟通是通过数据的。如果数据是包装作为一个匿名类型,如何做其他层次引用他呢?因为他们无法,匿名类型应被看作是技术供本地使用的唯一。
查询表达式
查询表达式是从关键字from以后,风格类似于sql的查询语句,我们得先声明system.query的命名空间。关键是我们怎么样写出一个正确的查询表达式。在sql中,查询语句是一个声明,来操作一个或多个表格,然后产生一个表格来返回,在linq中,查询表达式是一个声明的表达式,操作一个或多个IEnumerable对象,并返回一个IEnumerable对象。查询表达式还可以是一个迭代器的表达式,跨越一个或多个对象,产生一个对象包含你选择的结果,其返回的类型是一组IEnumerable,这样就能把数据很好的隐藏,同时转换成强类型,并且保证了安全和执行效率,我们可以通过迭代器来访问。
foreach (Doctor d in chicago);
可拓方法
查询表达式是通过可拓方法来把查询语句转换成传统的面向对象的方法的,这是c#3.0的新特性,在一个类中增加原来没有的函数。比如查询语句我们以前是这么写的
using System.Query;
// 查询所有住在chicago的医生
var chicago = from d in doctors
where d.City == "Chicago"
select d.Initials;
用可拓方法我们可以这样写
using System.Query;
var chicago = doctors.
Where(d => d.City == "Chicago").
Select(d => d.Initials);
这里面有两个方法where 和select他们并不在doctors这个类里面.
在c#3.0中可拓方法定义为静态方法,放在静态类里面,命名空间是
System.Runtime.CompilerServices.Extension通过引用这个命名空间,编译器会把可拓方法视为已经实例话的方法,比如在linq标准查询运算符,定义为可拓方法在System.Query.Sequence中通过引用System.Query我们使用查询运算符就和doctors里的函数一样。
namespace System.Query
{
public delegate ReturnT Func<ArgT, ReturnT>(ArgT arg);
public static class Sequence
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{ ... }
.
.
.
}
}
可拓方法where返回的是一个存在的IEnumerable对象的迭代器。应用一个基于Bool型的委托,来确定输出结果的条件。注意一下,返回的值,和第一个参数的值都是IEnumerable