【2017年整理】Lambda
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
达式
在我们程序中,经常有这样一些需求:
1. 需要一个临时方法,这个方法只会使用一次,或者使用的很少。 2. 这个方法的方法体很短,以至于比方法声明都短,写起来实在没劲(我将其称之为“一句话方法”)。
没办法,这样的方法写起来真是吃力不讨好,比如一些按钮事件处理中,有些按钮点击就是弹出一个对话框,或者调用一下别的什么方法。比如下面的代码: this.btnRefresh.Click += new System.EventHandler(this.btnRefre
sh_Click);
private void btnRefresh_Click(object sender, EventArgs e) {
BindData();
}
这个”Refresh”按钮就是做一下调用一下BindData()数据绑定的方法,为此我们不得不写一个新方法。好了,C# 2.0为我们提供了匿名方法:
this.btnRefresh.Click += delegate(object sender, EventArgs e) { BindData(); };
没劲的代码没了。想知道这种写法的幕后黑手么,
其实编译器还是在我们的后面干了一件龌龊的事情:它为我们产生了一个新的方法,它只是表面上为我们节省了代码。
privatevoid
b__0(object sender, EventArgs e) {
this.BindData();
}
看看这个编译器产生的方法的名称:
b_0,Test是这个匿名方法所放置的地方(因为这个按钮的时间我是放在一个Test方法里的)
还有一点需要注意的是,如果这个匿名方法是在实例方法里使用,那么编译器为我们生成的幕后方法也是实例方法,否则就是静态方法了.
是不是觉得匿名方法这东西很不错,减少了很多代码阿,但是匿名方法的使用还并不人性化,什么是人性化呢?比如你可以用自然的语言将程序代码读出来,这样才算人
性化了.在.net 2.0中System.Collections.Generic命名空间下List里有一些新增的方法。比如Find,如果使用匿名方法我们如何调用呢: books.Find(delegate(Book book){return book.Price < 50;}); 代码是很简单,但是却无法朗读出来,来看看Lambda表达式的写法: books.Find(book=>book.Price<50);这个Lambda表达式就可以这样阅读出来了:给你一本书,如果它的价格小于50则返回true。
好了,那我们就走进Lambda表达式吧:
将使用了Lambda表达式的程序集反编译后,我们发现,它实际上和匿名方法没有什么不同。Lambda的输入参数就对应着delegate括号里面的参数,由于Lambda表达式可以推断参数的类型,所以这里的参数无需声明。Lambda操作符读作”Goes to”,它后面紧跟着表达式或者是语句块(这点和匿名方法也不同,匿名方法只能使用语句块而不能使用表达式),下面我就用实例来
说明
关于失联党员情况说明岗位说明总经理岗位说明书会计岗位说明书行政主管岗位说明书
一下有那些类型的Lambda表达式: //x的类型省略了,编译器可以根据上下文推断出来,后面跟着的是表达式 x => x+1
deleage(int x){return x+1;}
//后面跟着的是语句块
x=>{return x+1;}
delegate(int x){return x+1;}
//输入参数也可以带类型,带类型后别忘记小括号哦
(int x) => x+1
delegate(int x){return x+1;}
//也可以多个输入参数,逗号分隔,别忘记小括号
(x,y) => x+y
delegate(int x,int y){return x+y;} //无参的也行
() => 1
delegate(){return 1;}
对于Lambda表达式来说她的用法就是如此,但是在Lambda背后却有很多的故事和玄机。用Lambda表达式可以构建表达式树,而表达式树对于Linq来说就像树根对于树一样重要。在这里就不讨论表达式树的问题了,这个东西也不是三言两语能够说清楚的,等待时机成熟的时候我们再来进一步讨论。
Lambda表达式更多阅读
Lambda实际上源远流长,我们现在使用的机器都是冯-诺依曼体系的,属于图灵机,在那之前还有一种称作λ演算的理论,但是图灵机由于先被实现出来,所以大行其道,λ演算后来成就了函数式编程语言特别是Lisp,在函数式编程语言里函数是第一等元素,函数的参数,函数的返回值都是函数,程序没有变量,函数嵌套函数。而且函数式编程语言一直存在于象牙塔中,所以在工业界并没有得到通用,不过近年来工业界比较喜欢“复古”风格,所以函数式编程语言也慢慢的走上了历史的舞台。函数式编程能解决一些命令式编程难以解决的问题(或者解决起来非常麻烦)。C#要做到函数风格编程怎么办,靠原来的方法定义的方式肯定是不可行的,2.0的匿名方法从某种程序上来说解决了这个问题,但还是不够,3.0里的Lambda终于很好的解决了,一个Lambda就是一个delegate,一个delegate指向一个方法,现在我们使用Lambda也能简单的将方法作为参数传递了,还可以层层嵌套,都是很简单的事情了。
什么是Lambda表达式,
随VS 2005发布的C#2.0引进了匿名方法的概念,允许在预期代理(delegate)值的地方用“行内(in-line)”代码块(code blocks)来做替代。
Lambda表达式为编写匿名方法提供了更简明的函数式的句法,但结果却在编写LINQ查询表达式时变得极其有用,因为它们提供了一个非常紧凑的而且类安全的方式来编写可以当作参数来传递,在以后作运算的函数。
Lambda表达式的例子:
在我以前的扩展方法博客贴子里,我演示了你如何可以象下面这样声明一个简单的Person类:
然后,我示范了你可以如何使用一些值来生成一个List集合的实例,然后使用由LINQ提供的新的Where和Average扩展方法来返回集合中的人的一个子集,以及计算这个集合中的人的平均年龄:
上面高亮标记的红色 p => 表达式就是Lambda表达式。在上面的例子里,我用第一个lambda来指定获取特定人时所用的过滤条件,用第二个lambda来指定在计算平均年龄时该用Person对象的哪个值。
详解Lambda表达式
理解Lambda表达式最容易的方法是把它们设想成编写简明的行内方法的方式。譬如,我上面编写的例子可以使用C#2.0的匿名方法来编写,象这样:
上 面两个匿名方法都接受一个Person类型的参数。第一个匿名方法返回一个布尔值,表示Person的LastName是否是Guthrie,第二个匿名 方法返回一个整数值(返回那个人的年龄)。我们前面使用的lambda表达式的作用是一样的,两个表达式都接受一个Person类型的参数。第一个 lambda表达式返回一个布尔值,第二个返回一个整数。
在C#里,一个lambda表达式在句法上是写成一个参数列表,随后是 => 符号,随后是表达式在调用时要运算的表达式或者语句块:
params => expression
所以,当我们编写这样的lambda表达式时:
p => p.LastName == “Guthrie”
我们是想表示,我们在定义的Lambda接受一个参数p,要运行的代码表达式返回p.LastName的值是否等于“Guthrie”。 我们将参数命名为p是不相干的,我也可以很容易地将其命名为o,x,foo,或者我想要的任何名字。
不 象匿名方法
要求
对教师党员的评价套管和固井爆破片与爆破装置仓库管理基本要求三甲医院都需要复审吗
参数类型是明确地指明的,Lambda表达式允许省略参数类型,而允许它们根据用法来推断出类型。譬如,当我编写 p=>p.LastName == “Guthrie” 这个lambda表达式时,编译器推断出p参数属于Person类型,因为当前的Where扩展方法的对象是个范型的List< Person>集合。
Lambda参数的类型可以在编译时和被Visual Studio的intellisense引擎推断出来,这意味着在编写lambda时你将获得完全的intellisense 和编译时检查。譬如,注意当我在下面健入
p. 时,Visual Studio Orcas是如何提供intellisense完成的,因为它知道 p 是 Person类型:
注: 假如你要给一个Lambda表达式明确地声明参数的类型的话,你可以在Lambda参数表里的参数名字前声明参数类型,象这样:
针对框架开发人员的高级内容:Lambda表达式树 (Lambda Expression Trees)
从 一个框架开发人员(framework developer)的角度来看,使得Lambda表达式特别强有力的事情之一是,它们既可以以基于IL的方法的形式被编译成代码代理(code delegate),或者也可以编译成一个表达式树(expression tree)对象,然后在运行时用来分析,转换或者优化表达式。
能将Lambda表达式编译成一个表达式树对象是个强大无比的机制,将促成许多使用场景,包括使用能提供编译时句法检查和VS intellisense的统一的查询语言来建立支持丰富数据查询的高性能对象映射器(无论是关系数据库,活动目录,还是web服务)之能力。 从Lambda表达式到代码代理 (Code Delegates)
上 面的Where扩展方法是个将Lambda表达式编译成代码代理(code delegate)的例子(意即它是编译成IL的,可以以代理的形式调用)。支持象上面那样过滤任何IEnumerable集合的Where()扩展方法 可以使用下面这样的扩展方法代码来实现:
上面的Where()扩展方法接受一个 Func 类型的过滤参数,该参数是个接受一个类型为T的参数,返回一个布尔值表示条件是否满足的方法之代理。当我们把Lambda表达式作为一个参数传递给这个 Where() 扩展方法时,C#编译器会将我们的Lambda表达式编译成IL方法代理(这里, 将是Person),然后我们的Where()方法可以调用来计算某个给定条件是否被满足了。
从Lambda表达式到表达式树
当我们要想针对类似我们的列表集合一样的内存中的数据做运算时,把lambda表达式编译成代码代理是恰如其分的。但考虑一下你想要查询数据库里的数据的情形(下面的代码是使用Orcas中内置的LINQ到SQL对象关系映射器写成的) :
这里,我要从数据库里取出一串强类型的Product对象,我向Where()扩展方法表示,要通过一个Lambda表达式来做过滤。
我绝对不想 要看到发生的是,从数据库里取回所有的产品记录,将它们放在一个局部的集合里,然后在内存里对它运行Where()扩展方法来进行过滤。这么做效率极其不 高,对大数据库的扩缩性将是极差的。而我希望的是,LINQ到SQL的ORM将我上面的Lambda过滤条件翻译成SQL表达式,然后在远程的数据库里进 行过滤性查询。那样的话,我只返回那些符合查询条件的记录,这样的数据库查询效率是非常高的。
框架开发人员可以通过声明他们的Lambda表达式参数是个Expression类型,而不是Func类型来取得这样的结果。这会导致Lambda表达式参数被编译成一个我们可以在运行时拆开和分析的表达式树:
注意上面我是怎么把我们在先前用过的同样的 p=>p.LastName == “Guthrie” Lambda
表达式,但这次将其赋值给一个 Expression> 变量,而不是Func 变量。编译器不会产生IL,而是会指派一个表达式树对象,然后我作为一个框架开发人员就可以用它来对相应的Lambda表达式进行分析,按我想要的方式对其进行运算(譬如,我可以挑出表达式中的类型,名字和值等)。
在LINQ到SQL的情形下,它会将这个Lambda过滤语句翻译成
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
的关系SQL语句,来对数据库进行操作(从逻辑上来说,一个“SELECT * from Products where UnitPrice < 55”
语句)。
IQueryable 接口
为 帮助框架开发人员建立可查询的数据提供器,LINQ提供了 IQueryable 接口。这个接口实现了标准的LINQ扩展方法查询运算符,提供了一个更便利的方式来实现对一个复杂的表
达式树的处理(譬如,象下面这样,我用了3个不同的 扩展方法,2个lambda来从数据库取回10个产品的情形):
匿名方法和Lambda表达式
在我们程序中,经常有这样一些需求:
1. 需要一个临时方法,这个方法只会使用一次,或者使用的很少。
2. 这个方法的方法体很短,以至于比方法声明都短,写起来实在没劲(我将其称之为“一句话方法”)。
没办法,这样的方法写起来真是吃力不讨好,比如一些按钮事件处理中,有些按钮点击就是弹出一个对话框,或者调用一下别的什么方法。比如下面的代码:
this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
private void btnRefresh_Click(object sender, EventArgs e) {
BindData();
}
这个”Refresh”按钮就是做一下调用一下BindData()数据绑定的方法,为此我们不得不写一个新方法。好了,C# 2.0为我们提供了匿名方法:
this.btnRefresh.Click += delegate(object sender, EventArgs e) { BindData
(); };
没劲的代码没了。想知道这种写法的幕后黑手么,
其实编译器还是在我们的后面干了一件龌龊的事情:它为我们产生了一个新的方法,它只是表面上为我们节省了代码。
privatevoidb__0(object sender, EventArgs e) {
this.BindData();
}
看看这个编译器产生的方法的名称:
b_0,Test是这个匿名方法所放置的地方(因为这个按钮的时间我是放在一个Test方法里的) 还有一点需要注意的是,如果这个匿名方法是在实例方法里使用,那么编译器为我们生成的幕后方法也是实例方法,否则就是静态方法了。
是不是觉得匿名方法这东西很不错,减少了很多代码阿,但是匿名方法的使用还并不人性化,什么是人性化呢?比如你可以用自然的语言将程序代码读出来,这样才算人性化了.在.net 2.0中System.Collections.Generic命名空间下List里有一些新增的方法。比如Find,如果使用匿名方法我们如何调用呢:
books.Find(delegate(Book book){return book.Price < 50;}); 代码是很简单,但是却无法朗读出来,来看看Lambda表达式的写法: books.Find(book=>book.Price<50);这个Lambda表达式就可以这样阅读出来了:给你一本书,如果它的价格小于50则返回true。
好了,那我们就走进Lambda表达式吧:
将使用了Lambda表达式的程序集反编译后,我们发现,它实际上和匿名方法没有什么不同。Lambda的输入参数就对应着delegate括号里面的参数,由于Lambda表达式可以推断参数的类型,所以这里的参数无需声明。
Lambda操作符读作”Goes to”,它后面紧跟着表达式或者是语句块(这点和匿名方法也不同,匿名方法只能使用语句块而不能使用表达式),下面我就用实例来说明一下有那些类型的Lambda表达式:
//x的类型省略了,编译器可以根据上下文推断出来,后面跟着的是表达式
//x的类型省略了,编译器可以根据上下文推断出来,后面跟着的是表达式 x => x+1
deleage(int x){return x+1;}
//后面跟着的是语句块
x=>{return x+1;}
delegate(int x){return x+1;}
//输入参数也可以带类型,带类型后别忘记小括号哦
(int x) => x+1
delegate(int x){return x+1;}
//也可以多个输入参数,逗号分隔,别忘记小括号
(x,y) => x+y
delegate(int x,int y){return x+y;}
//无参的也行
() => 1
delegate(){return 1;}
对于Lambda表达式来说她的用法就是如此,但是在Lambda背后却有很多的故事和玄机。用Lambda表达式可以构建表达式树,而表达式树对于Linq来说就像树根对于树一样重要。在这里就不讨论表达式树的问题了,这个东西也不是三言两语能够说清楚的,等待时机成熟的时候我们再来进一步讨论。
Lambda表达式更多阅读
Lambda实际上源远流长,我们现在使用的机器都是冯-诺依曼体系的,属于图灵机,在那之前还有一种称作λ演算的理论,但是图灵机由于先被实现出来,所以大行其道,λ演算后来成就了函数式编程语言特别是Lisp,在函数式编程语言里函数是第一等元素,函数的参数,函数的返回值都是函数,程序没有变量,函数嵌套函数。而且函数式编程语言一直存在于象牙塔中,所以在工业界并没有得到通用,不过近年来工业界比较喜欢“复古”风格,所以函数式编程语言也慢慢的走上了历史的舞台。函数式编程能解决一些命令式编程难以解决的问题(或者解决起来非常麻烦)。C#要做到函数风格编程怎么办,靠原来的方法定义的方式肯定是不可行的,2.0的匿名方法从某种程序上来说解决了这个问题,但还是不够,3.0里的Lambda终于很好的解决了,一个Lambda就是一个delegate,一个delegate指向一个方法,现在我们使用Lambda也能简单的将方法作为参数传递了,还可以层层嵌套,都是很简单的事情了
1.准备测试数据
static int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; static string[] strings = new string[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; class Person {
public string Name;
public int Level;
}
static Person[] persons = new Person[] {
new Person {Name="Matt", Level=3},
new Person {Name="Luca", Level=3},
new Person {Name="Jomo", Level=5},
new Person {Name="Dinesh", Level=3},
new Person {Name="Charlie", Level=3},
new Person {Name="Mads", Level=3},
new Person {Name="Anders", Level=9}
};
2.过滤数据
public static void Sample1() {
// use Where() to filter out elements matching a particular condition
IEnumerable fnums = numbers.Where(n => n < 5); Console.WriteLine("Numbers < 5");
foreach(int x in fnums) {
Console.WriteLine(x);
}
}
3.匹配首个字母
public static void Sample2() {
// use First() to find the one element matching a particular condition
string v = strings.First(s => s[0] == 'o'); Console.WriteLine("string starting with 'o': {0}", v); }
4.根据numbers排序
public static void Sample3() {
// use Select() to convert each element into a new value
IEnumerable snums = numbers.Select(n => strings[n]); Console.WriteLine("Numbers");
foreach(string s in snums) {
Console.WriteLine(s);
}
}
5.匿名类型,注意var关键字
public static void Sample4()
{
// use Anonymous Type constructors to construct multi-valued results on
the fly
var q = strings.Select(s => new {Head = s.Substring(0,1), Tail = s.Substring(1)});
foreach(var p in q) {
Console.WriteLine("Head = {0}, Tail = {1}", p.Head, p.Tail); }
}
6.联合查询(即使用两个以上的查询条件)
public static void Sample5() {
// Combine Select() and Where() to make a complete query
var q = numbers.Where(n => n < 5).Select(n => strings[n]); Console.WriteLine("Numbers < 5");
foreach(var x in q) {
Console.WriteLine(x);
}
}
7.使用ToList方法缓存数据,不知道这样说对不对
public static void Sample6() {
// Sequence operators form first-class queries are not executed until you
enumerate them.
int i = 0;
var q = numbers.Select(n => ++i);
// Note, the local variable 'i' is not incremented until each element is
evaluated (as a side-effect).
foreach(var v in q) {
Console.WriteLine("v = {0}, i = {1}", v, i); }
Console.WriteLine();
// Methods like ToList() cause the query to be executed immediately,
caching the results
int i2 = 0;
var q2 = numbers.Select(n => ++i2).ToList(); // The local variable i2 has already been fully incremented before we
iterate the results
foreach(var v in q2) {
Console.WriteLine("v = {0}, i2 = {1}", v, i2); }
}
8.分组查询
public static void Sample7() {
// use GroupBy() to construct group partitions out of similar elements
var q = strings.GroupBy(s => s[0]); // <- group by first character of each string
foreach(var g in q) {
Console.WriteLine("Group: {0}", g.Key); foreach(string v in g) {
Console.WriteLine("\tValue: {0}", v);
}
}
}
9.统计聚合
public static void Sample8() {
// use GroupBy() and aggregates such as Count(), Min(), Max(), Sum(),
Average() to compute values over a partition
var q = strings.GroupBy(s => s[0]).Select(g => new {FirstChar = g.Key, Count = g.Count()});
foreach(var v in q) {
Console.WriteLine("There are {0} string(s) starting with the letter {1}",
v.Count, v.FirstChar);
}
}
10.排序
// use OrderBy()/OrderByDescending() to give order to your resulting sequence
var q = strings.OrderBy(s => s); // order the strings by their name
foreach(string s in q) {
Console.WriteLine(s);
}
}
11.二次排序
public static void Sample9a() {
// use ThenBy()/ThenByDescending() to provide additional ordering detail
var q = persons.OrderBy(p => p.Level).ThenBy(p => p.Name); foreach(var p in q) {
Console.WriteLine("{0} {1}", p.Level, p.Name); }
}