LINQ学习参考资料.docx
《LINQ学习参考资料.docx》由会员分享,可在线阅读,更多相关《LINQ学习参考资料.docx(42页珍藏版)》请在冰豆网上搜索。
![LINQ学习参考资料.docx](https://file1.bdocx.com/fileroot1/2023-1/1/662483cc-8cc3-4931-84df-67f65a16b71a/662483cc-8cc3-4931-84df-67f65a16b71a1.gif)
LINQ学习参考资料
前沿技术课(NTC)LINQ学习参考资料
该参考资料共分为二大部份,包括DotNet新特性与LINQ,具体参考内容如下所示:
一、.NET3.5新特性
.NET3.5的新特性包括:
自动属性(Auto-ImplementedProperties)
隐含类型局部变量(LocalVariableTypeInference)
匿名类型(AnonymousTypes)
对象与集合初始化器(ObjectandCollectionInitializers)
扩展方法(ExtensionMethods)
Lambda表达式和Lambda表达式树(LambdaExpressionandLambdaExpressionTrees)
LINQ,语言级集成查询(LanguageINtegratedQuery)
自动属性(Auto-ImplementedProperties)
自动属性可以避免原来这样我们手工声明一个私有成员变量以及编写get/set逻辑,在VS2008中可以像下面这样编写一个类,编译器会自动地生成私有变量和默认的get/set操作。
你也可以分别定义get和set的"protected"等访问级别。
在.Net2.0框架下,我们可以这样写一个User类:
publicclassUser
{
privateint_id;
privatestring_name;
privateint_age;
publicintId
{
get{return_id;}
set{_id=value;}
}
publicstringName
{
get{return_name;}
set{_name=value;}
}
publicintAge
{
get{return_age;}
set{_age=value;}
}
}现在,可以这样简化:
publicclassUser
{
publicintId{get;set;}
publicstringName{get;set;}
publicintAge{get;set;}
}
我们可以利用VS.NET提供的ildasm进行反编译,可以发现.NET帮我定义了私有变量,在此不再赘言。
隐含类型局部变量(LocalVariableTypeInference)
C#3.0引进了var这个新关键字,在声明局部变量时可用于替代原先的类型名,即当一个变量声明标识为var类型并且该范围域中没有var名称类型存在,那么这个声明就称为隐含类型局部变量。
如下(等同于//后面的显式声明):
vari=5;//int
varj=23.56;//double
vark="CSharp";//string
varx;//错误
vary=null;//错误
varz={1,2,3};//错误
注意事项:
必须对var声明的变量赋值,因为C#中的var并不等同于VB于Javascript中的var,后者中的var为一种变量类型,而在C#中var并不是一种类型,它只用来隐式的声明变量,而你定义的变量类型由编译器判断;
Var声明的变量不能赋null;
Var只能声明局部变量;
不允许,改变var变量的类型
Varx=100;
X="Helloworld!
"
由此可知,var的变量也是强类型的变量,具有安全性。
匿名类型(AnonymousTypes)
匿名类型允许定义行内类型,无须显式定义类型。
常和var配合使用来声明匿名类型。
varp1=new{Id=1,Name="tony",Age=21};//属性也不需要申明
varp2=new{Id=2,Name="dream",Age=21};
varp3=new{Id=3,age=21,Name="tony"}
p1=p2;//p1,p2结构相同,可以互相赋值,p1与p3不是相同的对象,所以使用匿名类型时我们尽量将具有相同属性的变量,属性的顺序保持一致。
在这里编译器会认为p1,p2相当于:
publicclassSomeType
{
publicintId{get;set;}
publicstringName{get;set;}
publicintAge{get;set;}
}那么数组怎么定义呢?
使用"new[]"关键字来声明数组,加上数组的初始值列表。
像这样:
varintArray=new[]{2,3,5,6};
varstrArray=new[]{"Hello","World"};
varanonymousTypeArray=new[]
{
new{Name="tony",Age=21},
new{Name="dream",Age=22}
};
vara=intArray[0];
varb=strArray[0];
varc=anonymousTypeArray[1].Name;
可以使用new关键字调用匿名初始化器创建一个匿名类型的对象。
匿名类型直接继承自System.Object。
匿名类型的成员是编译器根据初始化器推断而来的一些读写属性。
对象初始化器(ObjectInitializers):
.NET2.0框架中的类型非常依赖于属性。
当生成对象实例和使用新的类型时,在.Net2.0时候我们像这样写:
Useruser=newUser();
user.Id=1;
user.Name="tony";
user.Age=22;在VS2008中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。
我们可以这样简化:
像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。
Useruser=newUser{Id=1,Name="tony",Age=21};又例如,我把二个人加到一个基于泛型的类型为User的List集合中:
Listuser=newList{
newUser{Id=1,Name="tony",Age=21},
newUser{Id=2,Name="dream",Age=12},
};
如果有相同名字和类型的两个对象初始化器将会产生相同的实例,可以相互赋值。
例如:
Useruser=newUser{Id=1,Name="tony",Age=21};
Useruser2=newUser{Id=2,Name="tony",Age=12};
user=user2;
除了在初始化类时设置简单的属性值外,对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。
例如我们可以在上面定义的User类型同时拥有一个属于School类型的叫"School"的属性:
Useruser=newUser
{
Id=1,
Name="tony",
Age=21,
School=newSchool
{
City="Beijing",
Name="BTBU"
}
};
集合初始化器(CollectionInitializers):
集合初始化器由一系列集合对象组成,用逗号间隔,使用{}封闭。
集合初始化器可以简化把几个对象一起添加到一个集合,编译器会自动为你做集合插入操作。
例如我把七个数加到一个基于泛型的类型为int的List集合中
Listnum=newList{0,1,2,6,7,8,9};对象与集合初始化器要点
对象初始化器实际上利用了编译器对对象中对外可见的字段和属性进行按序赋值。
对象初始化器允许只给一部分属性赋值,包括internal访问级别
对象初始化器可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行。
集合初始化器会对初始化器中的元素进行按序调用ICollection.Add(T)方法,所以只有具有Add方法的类才可以使用这种方法添加一个元素,例如ArrayList等,例如HashTable,字典等就不支持这种添加方式,因为其存在key,value两个值。
注意对象初始化器和集合初始化器中成员的可见性和调用顺序。
对象与集合初始化器同样是一种编译时技术。
扩展方法(ExtensionMethods)
往往我们需要对CLR类型进行一些操作,但苦于无法扩展CLR类型的方法,只能创建一些helper方法,或者继承类。
我们来修改上面的User类:
publicclassUser
{
publicintId{get;set;}
publicstringName{get;set;}
publicintAge{get;set;}
publicstringRead()
{
return"Id:
"+Id+"姓名:
"+Name+"年龄:
"+Age;
}
}然后调用
varuser=new{Id=1,Name="tony",Age=21};
varstr=user.Read();现在有了扩展方法就方便多了。
扩展方法允许开发人员往一个现有的CLR类型的公开契约(contract)中添加新的方法,而不用生成子类或者重新编译原来的类型。
扩展方法有助于把今天动态语言中流行的对ducktyping的支持之灵活性,与强类型语言之性能和编译时验证融合起来。
扩展方法是可以通过使用实例方法语法调用的静态方法。
效果上,使得附加的方法扩展已存在类型和构造类型成为可能。
他可以对现有类功能进行扩充,从而使该类型的实例具有更多的方法(功能)。
扩展方法允许我们在不改变源代码的情况下扩展(即添加不能修改)现有类型中的实例方法。
扩展方法给我们一个怎样的思路呢?
我们一步一步做一下!
首先声明扩展方法:
通过指定关键字this修饰方法的第一个参数。
注意扩展方法仅可声明在静态类中。
扩展方法具备所有常规静态方法的所有能力,可以使用实例方法语法来调用。
接着就可以调用扩展方法了。
下面通过一个具体的实例分析一下:
例如我们要检查一个字符串变量是否是合法的电子邮件地址?
在.Net2.0框架下像这样:
varemail="tony_wanghongchen@";
if(EmailValidator.IsValid(email))
{
Response.Write("tony提示:
这是一个正确的邮件地址");
}而使用扩展方法的话,我可以添加"IsValidEmailAddress()"方法到string类本身中去,该方法返回当前字符串实例是否是个合法的字符串。
if(email.IsValidEmailAddress())
{
Response.Write("tony提示:
这是一个正确的邮件地址");
}我们是怎么把这个IsValidEmailAddress()方法添加到现有的string类里去的呢?
先定义一个静态类,再定义"IsValidEmailAddress"这个静态的法来实现的。
publicstaticclassExtensions//静态类
{
publicstaticboolIsValidEmailAddress(thisstrings)
//静态方法和this
{
Regexregex=newRegex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
returnregex.IsMatch(s);
}
}注意,上面的静态方法在第一个类型是string的参数变量前有个"this"关键词,这告诉编译器,这个特定的扩展方法应该添加到类型为"string"的对象中去。
然后在IsValidEmailAddress()方法实现里,我可以访问调用该方法的实际string实例的所有公开属性/方法/事件,取决于它是否是合法电子邮件地址来返回true/false。
扩展方法不仅能够应用到个别类型上,也能应用到.NET框架中任何基类或接口上。
即可用于整个.NET框架丰富的可组合的框架层扩展。
扩展方法要点
扩展方法的本质为将实例方法调用在编译期改变为静态类中的静态方法调用。
事实上,它确实拥有静态方法所具有的所有功能。
扩展方法的作用域是整个namespace可见的,并且可以通过usingnamespace来导入其它命名空间中的扩展方法。
扩展方法的优先级:
现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后为较远的namespace下的静态类的静态方法。
扩展方法是一种编译时技术,注意与反射等运行时技术进行区别,并慎重使用。
Lambda表达式和Lambda表达式树(LambdaExpressionandLambdaExpressionTrees)
Lambda表达式
我们从"所有字符串查找包含tony子字符串"说起。
在C#2.0中,匿名方法允许我们以内联的方式来实现委托实例,它提供强大的函数式编程语言,但是标记显得相当的冗长和带有强制性。
我们使用C#2.0中的匿名方法查找,代码如下:
varinString=list.FindAll(delegate(strings)
{returns.Indexof("tony")>=0;});现在可以使用C#3.0带来的Lambda表达式允许我们使用一种更接近人的思维、更自然的方式来实现类似于匿名方法同样的效果,看下面的代码多么简洁:
varinString=list.FindAll(s=>s.Indexof("tony")>=0);Lambda表达式格式:
(参数列表)=>表达式或语句块
具体意义:
定义Lambda接受参数列表,运行表达式或语句块返回表达式或语句块的值传给这个参数列表。
Lambda表达式参数类型可以是隐式类型或显式类型。
在显式列表中,每个参数的类型是显式指定的,在隐式列表中,参数的类型由Lambda表达式出现的语境自动推断类型。
Lambda表达式的参数列表可以有一个或多个参数,或者无参数。
在有单一的隐型参数的lambda表达式中,圆括号可以从参数列表中省略。
例如:
(x,y)=>x*y;//多参数,隐式类型=>表达式
x=>x*10;//单参数,隐式类型=>表达式
x=>{returnx*10;};//单参数,隐式类型=>语句块
(intx)=>x*10;//单参数,显式类型=>表达式
(intx)=>{returnx*10;};//单参数,显式类型=>语句块
()=>Console.WriteLine();//无参数下面看这个例子:
在前面的帖子中,我们写了一个User类及增加了2个人,接下来,我们使用由LINQ提供的新的Where和Average方法来返回集合中的人的一个子集,以及计算这个集合中的人的平均年龄:
Listuser=newList{
newUser{Id=1,Name="tony",Age=21},
newUser{Id=2,Name="tony",Age=22},
};
//获取特定人时所用的过滤条件,p参数属于User类型
varresults=user.Where(p=>p.Name=="tony").ToList();
//用User对象的Age值计算平均年龄
varaverage=user.Average(p=>p.Age);
LINQ,语言级集成查询(LanguageINtegratedQuery)
经过了最近20年,面向对象编程技术(object-oriented(OO)programmingtechnologies)在工业领域的应用已经进入了一个稳定的发展阶段。
程序员现在都已经认同像类(classes)、对象(objects)、方法(methods)这样的语言特性。
考察现在和下一代的技术,一个新的编程技术的重大挑战开始呈现出来,即面向对象技术诞生以来并没有解决降低访问和整合信息数据(accessingandintegratinginformation)的复杂度的问题。
其中两个最主要访问的数据源与数据库(database)和XML相关。
LINQ提供了一条更常规的途径即给.NetFramework添加一些可以应用于所有信息源(allsourcesofinformation)的具有多种用途(general-purpose)的语法查询特性(queryfacilities),这是比向开发语言和运行时(runtime)添加一些关系数据(relational)特性或者类似XML特性(XML-specific)更好的方式。
这些语法特性就叫做.NETLanguageIntegratedQuery(LINQ)。
包含DLinq和XLinq
C#3.0LINQ查询语法
首先来看一个很简单的LINQ查询例子,查询一个int数组中小于5的数字,并按照大小顺序排列:
classProgram
{
staticvoidMain(string[]args)
{
int[]arr=newint[]{8,5,89,3,56,4,1,58};
varm=fromninarrwheren<5orderbynselectn;
foreach(varninm)
{
Console.WriteLine(n);
}
Console.ReadLine();
}
}
上述代码除了LINQ查询语法外,其他都是我们所熟悉的语法,而LINQ查询语法跟SQL查询语法很相似,除了先后顺序。
Q:
为何LINQ查询语法是以from关键字开头的,而不是以select关键字开头的?
select开头这种写法跟SQL的写法更接近,更易懂呀?
A:
简单来说,为了IDE的智能感知(InteliSence)这个功能,select关键字放在后面了。
编程语言以select开头写LINQ查询语法不是没出现过,你如果使用过2005年的VB9CTP版本,那时候VB9的LINQ查询语法就是select关键字在前面,但是select关键字在前面,在做智能感知(InteliSence)时候就很头大。
经过微软IDE组的权衡,确定了把from关键字放在最前面。
我们再来看一个稍稍复杂的LINQ查询:
在我们罗列的语言字符串中,我们希望按照字符长短,分类罗列出来,实现代码如下:
staticvoidMain(string[]args)
{
string[]languages={"Java","C#","C++","Delphi","VB.net","VC.net","C++Builder","Kylix","Perl","Python"};
varquery=fromiteminlanguages
orderbyitem
groupitembyitem.LengthintolengthGroups
orderbylengthGroups.Keydescending
selectlengthGroups;
foreach(variteminquery)
{
Console.WriteLine("stringsoflength",item.Key);
foreach(varvalinitem)
{
Console.WriteLine(val);
}
}
Console.ReadLine();
}
其中的into关键字表示将前一个查询的结果视为后续查询的生成器,这里是跟groupby一起使用的。
LINQ中的Groupby不要跟SQL中的Groupby混淆,SQL由于是二维结构,Groupby的一些逻辑受二维结构的约束,无法象LINQ中的Groupby这么灵活。
LINQ
二、LINQ
1、查询表达式(LINQ)简介
LINQ是LanguageIntegratedQuery的简称,它是集成在.NET编程语言中的一种特性。
已成为编程语言的一个组成部分,在编写程序时可以得到很好的编译时语法检查,丰富的元数据,智能感知、静态类型等强类型语言的好处。
并且它同时还使得查询可以方便地对内存中的信息进行查询而不仅仅只是外部数据源。
LINQ定义了一组标准查询操作符用于在所有基于.NET平台的编程语言中更加直接地声明跨越、过滤和投射操作的统一方式,标准查询操作符允许查询作用于所有基于IEnumerable接口的源,并且它还允许适合于目标域或技术的第三方特定域操作符来扩大标准查询操作符集,更重要的是,第三方操作符可以用它们自己的提供附加服务的实现来自由地替换标准查询操作符,根据LINQ模式的习俗,这些查询喜欢采用与标准查询操作符相同的语言集成和工具支持。
我们来总体看看LINQ架构
在.NET3.5下,微软为我们提供了一些命名空间
LINQ包括五个部分:
LINQtoObjects、LINQtoDataSets、LINQtoSQL、LINQtoEntities、LINQtoXML。
LINQtoSQL全称基于关系数据的.NET语言集成查询,用于以对象形式管理关系数据,并提供了丰富的查询功能。
其建立于公共语言类型系统中的基于SQL的模式定义的集成之上,当保持关系型模型表达能力和对底层存储的直接查询评测的性能时,这个集成在关系型数据之上提供强类型。
LINQtoXML在System.Xml.LINQ命名空间下实现对XML的操作。
采用高效、易用、内存中的XML工具在宿主编程语言中提供XPath/XQuery功能等。
说了这么多,我们还是用一个简单的实例说明一下微软LINQtoSQL框架为我们带来的体验。
LINQtoSQL实例
第一步:
建立dbml(DatabaseMarkLanguage。
数据库描述语言,是一种xml格式的文档,用来描述数据库)文件,以Northwind数据库为例,上述Customers类被映射成一个表,对应数据库中的Customers表
第二步:
创建一个ASP.NET页面,在页面上加入一个GridView控件
第三步:
编写代码进行数据绑定
第四步:
运行显示结果。
好了,就说这么多吧,大家应该对LINQ有了总体的了解。
最后我对LINQ还有一点疑惑,在此提出,请熟悉的朋友来探讨:
1.LINQ是在ADO.NET之上的,那么在将来它会代替ADO.NET吗?
2.在大型项目中使用LINQ,它的效率如何呢?
2、Linqtosql:
DataContext与实体
DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询句法翻译成SQL语句,以及把数据从数据库返回给调用方和把实体的修改写入数据库。
DataContext提供了以下一些使用的功能:
*以日志形式记录DataContext生成的SQL
*执行SQL(包括查询和更新语句)
*创建和删除数据库
DataContext是实体和数据库之间的桥梁,那么首先我们需要定义映射到数据表的实体。
定义实体类
usingSystem.Data.Linq.Mapping;
[Table(Name="Customers")]
publicclassCustomer
{
[Column(IsPrimaryKey=true)]
publicstringCustomerID{get;set;}
[Column(Name="ContactName")]
publicstringName{get;set;}
[Column]
publicstringCity{get;set;}
}
以Northwind数据库为例,上述Customers类被映射成一个表,对应数据库中的Customers表。
然后在类型中定义了三个属性,对应表中的三个字段。
其中,CustomerID字段是主键,如果没有指定Column特性的Name属性,那么系统会把属性名作为数据表的