LINQ概述#

  1. 语言集成查询(Language intergrated Query,LINQ)在C#编程语言中集成了查询语法。
  2. 可以使用相同的语法访问不同的数据源
  3. 提供了不同数据源的抽象层,所有可以使用相同的语法

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笔记的更多相关文章

  1. 简单的Linq笔记

    最近带一个新人,被问到Linq的一点东西,回答他后,自己记录下,防止自己懵逼. Linq中查询一个表中指定的几个字段: var ts = t.FindAllItems().Where(P => ...

  2. 简单Linq笔记

    Linq是.net 3.5才引入的 要引入命名空间System.Linq. Linq  to XML要引入System.Xml.Linq Linq to ADO.NET要引入System.Data.L ...

  3. C#高级编程(第9版) 第11章 LINQ 笔记

    概述语言集成查询(Language Integrated Query, LINQ)在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源.LINQ提供了不同数据源的抽象层,所以可以使用相同的 ...

  4. 《C#本质论》读书笔记(15)使用查询表达式的LINQ

    15.1 查询表达式的概念 简单的查询表达式 private static void ShowContextualKeywords1() { IEnumerable<string> sel ...

  5. linq学习笔记

    最近在学习linq的一些基础知识,看了c#高级编程及阅读了园子内部几篇优秀的博文,有所体会,感觉应该记录下来,作为以后复习使用.都是一些最基础的知识,大致分为三个部分:linq预备知识:linq查询: ...

  6. 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 ...

  7. Silverlight项目笔记1:UI控件与布局、MVVM、数据绑定、await/async、Linq查询、WCF RIA Services、序列化、委托与事件

    最近从技术支持转到开发岗,做Silverlight部分的开发,用的Prism+MVVM,框架由同事搭好,目前做的主要是功能实现,用到了一些东西,侧重于如何使用,总结如下 1.UI控件与布局 常用的主要 ...

  8. Linq to XML 读取XML 备忘笔记

    本文转载:http://www.cnblogs.com/infozero/archive/2010/07/13/1776383.html Linq to XML 读取XML 备忘笔记 最近一个项目中有 ...

  9. asp.net、mvc、ajax、js、jquery、sql、EF、linq、netadvantage第三方控件知识点笔记

    很简单,如下: 父页面:(弹出提示框) function newwindow(obj) { var rtn = window.showModalDialog('NewPage.htm','','sta ...

随机推荐

  1. JavaScript基础:创建对象

    先来看两种简单的对象创建方式: 1.Object构造函数方法 var person = new Object(); person.name = "Nicholas"; person ...

  2. java操作redis redis连接池

    redis作为缓存型数据库,越来越受到大家的欢迎,这里简单介绍一下java如何操作redis. 1.java连接redis java通过需要jedis的jar包获取Jedis连接. jedis-2.8 ...

  3. iOS 图片压缩方法

    iOS 图片压缩方法 两种图片压缩方法 两种压缩图片的方法:压缩图片质量(Quality),压缩图片尺寸(Size). 压缩图片质量 NSData *data = UIImageJPEGReprese ...

  4. MySQL5.7免安装教程

    注如果连文件位置都和我这个一样的话,基本上所有命令都可以直接复制这上面就行,前提是你愿意放到C盘的并在Program files下面新建一个文件夹mysql存放这些东西 建议大家还是自己动手配置一下这 ...

  5. 【鸡年大吉】,不知道写点啥,放个demo(小球碰撞)吧,有兴趣的看看

    最初的想法是仿写win7的泡泡屏保效果,但是对于小球的斜碰问题一直没搞明白(如果你会这个,欢迎留言或者做个demo),所以只是简单处理了碰撞后的速度,有时候会看起来很搞笑~~~funny guy 话不 ...

  6. 《JAVASCRIPT高级程序设计》表单基础知识和文本框脚本

    在HTML中,表单是由<form>元素来表示,在javascript中,表单对应的是HTMLFormElement类型,它具有一些独有的属性和方法: 一.表单基础知识 1.取得表单的方式 ...

  7. java_XML_比较【转】

    JAVA解析XML的方式DOM.SAX.DOM4J.JDOM.StAX之详解与比较 1.各种方式的详解 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官 ...

  8. matlab 向量法建数组(推荐)

    一.用赋值的方法可以扩展一个已知的数组: arr= 1:1:4; arr(8)=6; 此时,arr = 1 2 3 4 0 0 0 6 arrNew=arr; 此时arrNew = 1 2 3 4 0 ...

  9. (转)java二维数组的深度学习(静态与动态)

    转自:http://developer.51cto.com/art/200906/128274.htm,谢谢 初始化: 1.动态初始化:数组定义与为数组分配空间和赋值的操作分开进行:2.静态初始化:在 ...

  10. 非名校毕业年薪20W程序员 心得分享

    大家好,我是落落http://blog.csdn.net/robinson1988/ QQ692162374,其实我选择Oracle是一件很神奇的事情,但是现在回想起来又觉得是命中注定.我2006年就 ...