LINQ笔记
LINQ概述#
- 语言集成查询(Language intergrated Query,LINQ)在C#编程语言中集成了查询语法。
- 可以使用相同的语法访问不同的数据源
- 提供了不同数据源的抽象层,所有可以使用相同的语法
LINQ查询
var query= from r in [list] where r.x=='xxx' orderby r.x desceding select r;
查询表达式必须以from子句开头,以select或者group 结束 变量query只是指定了查询,查询语句不是通过赋值来执行,只要是使用了foreach循环查询,才正式执行
扩展方法
编译器会转换LINQ查询,以调用方法而不是LINQ查询。LINQ为IEnumberable接口提供了各种扩展方法,已便用户在实现该接口的任意集合上使用LINQ查询。
扩展方法可以吧方法添加到实现某个特性接口的任何类中,这样多个类就可以使用相同的实现代码。
public static class StringExtension
{
public static void Foo(this string s)
{
System.Console.WriteLine("Foo invoked for {0}",s);
}
}
string s="Hello";
s.Foo();
扩展方法不能访问它的扩展类型的私有成员。调用扩展方法只是调用静态方法的一种新语法
string s="Hello";
StringExtension.Foo(s);
只需要导入包含该类的命名空间就使用扩展方法
定义LINQ扩展方法的一个是在System.Linq命名空间Enumerable.
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
yield 关键字向编译器指示它所在的方法是迭代器块。编译器生成一个类来实现迭代器块中表示的行为。在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。yield 关键字也可与 break 结合使用,表示迭代结束。
例子:
yield return ;
yield break;在 yield return 语句中,将计算 expression 并将结果以值的形式返回给枚举器对象;expression 必须可以隐式转换为 yield 类型的迭代器。
在 yield break 语句中,控制权将无条件地返回给迭代器的调用方,该调用方为枚举器对象的 IEnumerator.MoveNext 方法(或其对应的泛型 System.Collections.Generic.IEnumerable)或 Dispose 方法。
方法返回IEnumerable,所有可以使用前面的结果依次调用这些方法。
Func是系统定义的泛型委托 public delegate TResult Func<in T, out TResult>(T arg);
推迟查询执行
在运行期间定义的查询表达式时,查询不会立刻运行。查询会在迭代数据项是运行。
var names = new List<string> {"Nino","Windy","Juan" };
//延时查询的案例
var namesWithJ = from n in names
where n.StartsWith("J")
orderby n
select n;
Console.WriteLine("First iteration");
//每次迭代才会执行查询
foreach( var name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
names.Add("John");
names.Add("Jim");
Console.WriteLine("Second iteration");
//每次迭代才会执行查询
foreach (var name in namesWithJ)
{
Console.WriteLine(name);
}
Console.ReadLine();
当然,每次在迭代是才使用查询,调用扩展方法,在大多数使用很有效,但是有些情况下我们想立刻执行,可以调用扩展方法ToArray()、ToList()
两次迭代之前输出保持不变,但集合中的值已经改变了。
标准的查询操作符
在Enumerable类定义很多标准查询操作符
筛选
使用where 子句,可以合并多个表达式,传递给where子句的表示结果类型应该是布尔类型:
var namesWithJ = from n in names
//组合表达式,返回布尔类型即可
where n.StartsWith("J") && n.Length > 3
orderby n
select n;
映射到扩展方法Where()和Select(),以下代码会与前面的LINQ查询非常类似的结果:
var namesWithJV2 = names.
Where(n => n.StartsWith("J") && n.Length > 3).
Select(n => n);
用索引筛选
不能使用LINQ查询是Where方法的重载,可以传递int参数--索引。可以在表达式中使用这个索引,执行基于索引的计算。
//
// 摘要:
// 基于谓词筛选值序列。将在谓词函数的逻辑中使用每个元素的索引。
//
// 参数:
// source:
// 要筛选的 System.Collections.Generic.IEnumerable<T>。
//
// predicate:
// 用于测试每个源元素是否满足条件的函数;该函数的第二个参数表示源元素的索引。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,包含输入序列中满足条件的元素。
//
// 异常:
// System.ArgumentNullException:
// source 或 predicate 为 null。
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);
案例(LINQ无法实现,Where方法可以实现)
var namesWithJV2 = names.
Where((n, index) => n.StartsWith("J") && n.Length > 3 && index % 2 != 0).
Select(n => n);
类型筛选
为了进行基于类型的筛选,可以使用OfType()扩展方法。
object[] data = { "one", 2, 3, "four", "five", 6 };
//筛选出类型是string的值
var query = data.OfType<string>();
foreach (var s in query)
{
Console.WriteLine(s);
}
复合的from子句
如果需要根据对象的一个成员进行筛选,而且该成员本身是一个系列,就可以使用复合from子句。
//复合from语句
var namesWithJ = from n in names
from c in n.ToCharArray()
where c.Equals('N')
orderby n
select n;
C#编译器吧复合的from子句和LINQ查询转换为SelectMany()扩展方法,该方法用于迭代序列的序列。
//
// 摘要:
// 将序列的每个元素投影到 System.Collections.Generic.IEnumerable<T>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数。
//
// 参数:
// source:
// 一个要投影的值序列。
//
// collectionSelector:
// 一个应用于输入序列的每个元素的转换函数。
//
// resultSelector:
// 一个应用于中间序列的每个元素的转换函数。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// TCollection:
// collectionSelector 收集的中间元素的类型。
//
// TResult:
// 结果序列的元素的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,其元素是对 source 的每个元素调用一对多转换函数
// collectionSelector,然后将那些序列元素中的每一个及其相应的源元素映射为结果元素的结果。
//
// 异常:
// System.ArgumentNullException:
// source 或 collectionSelector 或 resultSelector 为 null。
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
第一个参数是隐试参数,第二是collectionSelector委托,其中定义内部序列,第三是resultSelector委托,对于第二个序列调用该委托。
暂时没有理解SelectMany方法,后续再研究
排序
要对序列排序,使用orderby子句。
orderby子句解析为OrderBy()方法。
orderby descending对应OrderByDescending()方法。
两个方法返回的都是IOrderedEnumerable,该接口派生 IEnumerable
。
增加CreateOrderedEnumerable方法。用于进一步给序列排序。可以使用ThenBy()继续排序。
在linq查询是,只需要把所有用户排序的关键字(用逗号分隔开)添加到orderby子句中。
分组
要根据一个关键字值对查询结果分组,可以使用group子句。
内连接
使用join子句可以根据特点的条件合并两个数据源,但之前要获得两个要连接的列表。
左外连接
左外连接返回左边序列中全部元素,即使它在右边的序列中没有匹配的元素。
左外连接使用join子句和DefaultifEmpty方法定义。
分区
扩展方法Take()和Skip()等分区操作可用于分页。
Skip():先忽略根据的页面大小和实际页面计算的项数,
Take():提取一点数量的项
聚合操作符
聚合操作符(Count,Sum,Min,Max,Average,Aggergate)不返回一个序列,而返回一个值。
因为同一个查询需要使用相同的计算超过一次,所以是let子句定义一个变量。
Aggregate()方法,可以传递一个lambda表达式,该表达式对所有值进行聚合。
聚合函数会影响linq的延时加载特性。
转换操作符
在前面提到,查询可以推迟到访问数据项时再执行。再迭代中使用查询时,查询才会执行。而使用转换操作符会立即执行查询,把查询结果放在数组、列表或者字典中。
把返回的对象放在列表中并没有那么简单。对于复杂的可以使用新类Lookup<>来操作。
如需要对非类型泛化的集合上使用LINQ查询们可以使用Case方法。
生成操作符
生成操作符Range()、Empty()、Repear()不是扩展方法,而是返回序列的正常静态方法。
Enumerable.Range(1,20):1 2 3 .....20;
Empty 返回一个不返回值的迭代器;
Repeat方法返回一个迭代器;
表达式树
在LINQ TO objects中,扩展方法需要将一个委托类型作为参数,这样就可以将lambda表达式赋予参数。
lambda表达式也可以赋予Expression类型的参数。C#编译器根据类型给lambda表达式定义的不同行为。如果类型是Expression,编译器就从lambda表达式中创建一个表达式树,并存储在程序集中。这样就可以在运行期间分享表达式树,并进行优化,以便于查询数据源。
在Enumberable类不是唯一定义扩展方法Where()的类。在Queryable类也定义了Where()扩展方法。两者定义是不一样的。
// 摘要:
// 基于谓词筛选值序列。
//
// 参数:
// source:
// 要筛选的 System.Linq.IQueryable<T>。
//
// predicate:
// 用于测试每个元素是否满足条件的函数。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// 返回结果:
// 一个 System.Linq.IQueryable<T>,包含满足由 predicate 指定的条件的输入序列中的元素。
//
// 异常:
// System.ArgumentNullException:
// source 或 predicate 为 null。
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
//Enumerable
// 摘要:
// 通过合并元素的索引将序列的每个元素投影到新表中。
//
// 参数:
// source:
// 一个值序列,要对该序列调用转换函数。
//
// selector:
// 一个应用于每个源元素的转换函数;函数的第二个参数表示源元素的索引。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// TResult:
// selector 返回的值的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,其元素为对 source 的每个元素调用转换函数的结果。
//
// 异常:
// System.ArgumentNullException:
// source 或 selector 为 null。
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector);
Queryable定义的委托类型是Expression。
除了使用委托之外,编译器还会把表达式树放在程序集中。表达式树可以在运行期间读取。
表达式树派生自抽象基类Expression的类中构建。
创建一个表达式树,并将该树存储在程序集中。之后在运行期间使用这个树,创建一个应用于底层数据源的优化查询。
使用Expression类型的一个是ADO.NET Entity Framework 和WCF的数据服务的客户端提供程序。这样访问数据库的LINQ提供程序就可以读取表达式,创建一个运行期间优化的查询,从数据库中获取数据。
无论是用Func还是Expression参数传递,lambda表达式都是相同的。只是编译器的行为不同,它根据source参数来选择。在EF中返回的实现了IQueryable接口的ObjectQuery对象,因此EF中是Queryable类的扩展方法。
参考图书《C#高级编程》
推进阅读 http://www.cnblogs.com/jesse2013/p/happylambda.html
LINQ笔记的更多相关文章
- 简单的Linq笔记
最近带一个新人,被问到Linq的一点东西,回答他后,自己记录下,防止自己懵逼. Linq中查询一个表中指定的几个字段: var ts = t.FindAllItems().Where(P => ...
- 简单Linq笔记
Linq是.net 3.5才引入的 要引入命名空间System.Linq. Linq to XML要引入System.Xml.Linq Linq to ADO.NET要引入System.Data.L ...
- C#高级编程(第9版) 第11章 LINQ 笔记
概述语言集成查询(Language Integrated Query, LINQ)在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源.LINQ提供了不同数据源的抽象层,所以可以使用相同的 ...
- 《C#本质论》读书笔记(15)使用查询表达式的LINQ
15.1 查询表达式的概念 简单的查询表达式 private static void ShowContextualKeywords1() { IEnumerable<string> sel ...
- linq学习笔记
最近在学习linq的一些基础知识,看了c#高级编程及阅读了园子内部几篇优秀的博文,有所体会,感觉应该记录下来,作为以后复习使用.都是一些最基础的知识,大致分为三个部分:linq预备知识:linq查询: ...
- Dynamic CRM 2013学习笔记(三十)Linq使用报错 A proxy type with the name account has been defined by another assembly
在CRM中使用linq时,有时会报这个错误: A proxy type with the name account has been defined by another assembly. Curr ...
- Silverlight项目笔记1:UI控件与布局、MVVM、数据绑定、await/async、Linq查询、WCF RIA Services、序列化、委托与事件
最近从技术支持转到开发岗,做Silverlight部分的开发,用的Prism+MVVM,框架由同事搭好,目前做的主要是功能实现,用到了一些东西,侧重于如何使用,总结如下 1.UI控件与布局 常用的主要 ...
- Linq to XML 读取XML 备忘笔记
本文转载:http://www.cnblogs.com/infozero/archive/2010/07/13/1776383.html Linq to XML 读取XML 备忘笔记 最近一个项目中有 ...
- asp.net、mvc、ajax、js、jquery、sql、EF、linq、netadvantage第三方控件知识点笔记
很简单,如下: 父页面:(弹出提示框) function newwindow(obj) { var rtn = window.showModalDialog('NewPage.htm','','sta ...
随机推荐
- PHP使用hash_algos函数计算哈希值,之间的性能排序
PHP从5.1.2版本以上开始支持hash_algos函数,看这个名字就知道了,algos在英文中也表示算法的意思,hash_algos就是哈希算法,收集了一些常用的哈希算法,从5.1.2开始不同版本 ...
- Swiper --移动端触摸滑动插件
Swiper使用方法 1.首先加载插件,需要用到的文件有swiper.min.js和swiper.min.css文件. <!DOCTYPE html> <html> <h ...
- netty 对 protobuf 协议的解码与包装探究(2)
netty 默认支持protobuf 的封装与解码,如果通信双方都使用netty则没有什么障碍,但如果客户端是其它语言(C#)则需要自己仿写与netty一致的方式(解码+封装),提前是必须很了解net ...
- 深圳尚学堂:Web程序员应该会的知识
互联网的行业里涌入了很多的程序员, 都在为互联网的发展添砖加瓦.程序员可以分为很多种,像Unix程序员.Windows程序员,或是C++程序员.Delphi程序员,等等.今天我们谈谈Web程序员,一名 ...
- Professional C# 6 and .NET Core 1.0 - 37 ADO.NET
本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - 37 ADO.NET -------- ...
- C++编程练习(10)----“图的最小生成树“(Prim算法、Kruskal算法)
1.Prim 算法 以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树. 2.Kruskal 算法 直接寻找最小权值的边来构建最小生成树. 比较: Kruskal 算法主要是针对边来展开,边数 ...
- 康复计划#1 再探后缀自动机&后缀树
本篇口胡写给我自己这样的东西都忘光的残废选手 以及那些刚学SAM,看了其他的一些东西并且没有完全懵逼的人 (初学者还是先去看有图的教程吧,虽然我的口胡没那么好懂,但是我觉得一些细节还是讲清楚了的) 大 ...
- C++虚表(V-Table)解析
C++中的虚函数的作用主要是实现了多态,本人通过代码验证的方式了解虚表的结构及在多种继承方式下通过虚表访问子类函数.验证结果如下: 1)无虚函数覆盖的一般继承:可以通过子类的虚表访问父类的函数 2)虚 ...
- api接口json串换行
1.问题描述:在后台输入框中明明回车换行了,但是返回到 app客户端显示出来的 确实带有 \n 这个时候无论怎么调试都不行: 2.铺垫:大家都知道 php输出字符串的时候 使用 单引号 比使用 双 ...
- jQuery中绑定事件bind() on() live() one()的异同
jQuery中绑定事件的四种方法,他们可以同时绑定一个或多个事件 bind()-------------------------版本号小于3.0(在Jquery3.0中已经移除,相应unbind()也 ...