Linq快速入门——Lambda表达式的前世今生
Linq快速入门——Lambda表达式的前世今生
Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托。
何为委托
委托非常好理解,类似于C++里面的函数指针(指向了一个方法),并且委托约束了待指向方法的签名(由返回类型和参数组成)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace 委托Test
{
delegate bool FilterDelegate(int i);
class Program
{
static void Main(string[] args)
{
int[] array = { , , , , , , , , };
List<int> newList = MyFilter(array,FilterOdd);
foreach (int item in newList)
{
Console.WriteLine(item);
}
Console.ReadKey(); }
static List<int> MyFilter(int[] array, FilterDelegate filter)
{
List<int> list = new List<int>();
for (int i = ; i < array.Length; i++)
{
if (filter(i))
{
list.Add(i);
}
}
return list;
}
/// <summary>
/// 偶数
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
static bool FilterEven(int i)
{
return i % == ;
}
/// <summary>
/// 奇数
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
static bool FilterOdd(int i)
{
return i % == ;
}
}
}

对于上面这个Demo可以看出,我需要定义了两个方法(FilterOdd,FilterEven),让我的委托变量指向这两个方法。但有时候申明方法很麻烦,还要考虑方法名称不重复,所以对于一些我们只使用一次的方法,完全没有必要单独为其申明,使用匿名方法即可(C# 2.0为程序员提供了匿名方法),大大简化了操作
匿名方法
//例如
delegate void Del(int x);
....
Del d = delegate(int k) { /* ... */ };
所以上面例子小小改动一下即可:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace 委托Test
{
delegate bool FilterDelegate(int i);
class Program
{
static void Main(string[] args)
{
int[] array = { , , , , , , , , };
//使用匿名方法来求偶数
List<int> newList = MyFilter(array, delegate(int i) { return i % == ;
}); foreach (int item in newList)
{
Console.WriteLine(item);
}
Console.ReadKey(); }
static List<int> MyFilter(int[] array, FilterDelegate filter)
{
List<int> list = new List<int>();
for (int i = ; i < array.Length; i++)
{
if (filter(i))
{
list.Add(i);
}
}
return list;
}
}
}

Lambda表达式特性
- C# 2.0中加入的匿名方法,简化了我们编写事件处理函数的工作,使我们不再需要单独声明一个函数来与事件绑定,只需要使用delegate关键字在线编写事件处理代码。
- 而C# 3.0则更进一步,通过Lambda表达式,我们可以一种更为简洁方式编写事件处理代码,新的Lambda事件处理代码看上去就像一个计算表达式,它使用"=>"符号来连接事件参数和事件处理代码。我可以这样写:SomeEvent += 事件参数 => 事件处理代码;
所以上面代码稍稍修改后,用Lambda表达式来替换匿名方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace 委托Test
{
delegate bool FilterDelegate(int i);
class Program
{
static void Main(string[] args)
{
int[] array = { , , , , , , , , };
//使用Lambda表达式来求偶数
List<int> newList = MyFilter(array, i => i % ==); foreach (int item in newList)
{
Console.WriteLine(item);
}
Console.ReadKey(); }
static List<int> MyFilter(int[] array, FilterDelegate filter)
{
List<int> list = new List<int>();
for (int i = ; i < array.Length; i++)
{
if (filter(i))
{
list.Add(i);
}
}
return list;
}
}
}

注意:
- 使用Lambda表达式,"=>"之前为参数列表,如果有多个参数,则不能省略括号,比如:(s,e)=>....
- 如果方法有返回值,并且处理代码只有一行,可以简写成i=>i%2==0,等价于i=>{return i%2==0},反之对于有多行的处理代码,则不能简写,必须写完整,比如:(s,e)=>{...程序代码块...}
我们再来看看System.Linq名称空间下的扩展方法有什么特征:

第一个参数为扩展方法,我已经在前一篇文章《Linq快速入门——扩展方法》里提到了,我不做具体解释了,简单来说创建扩展方法就是这四步:
- 创建一个名为MyHelper的类,约定了此类中的方法均是扩展方法。注意这个类必须是静态类(Static)
- 扩展方法必须是Static静态方法
- 第一个参数为待扩展的类型,前面标注this
- 如果MyHelper在一个类库中,记得对其添加引用并using相关名称空间
对于第二个参数:System.Func<TSource, bool> predicate),我们再来深究下。
Fun<T,TResult> and Action<T>
- Fun<T,TResult>:此委托封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。所以在使用 Func<T, TResult> 委托时,不必显式定义一个封装只有一个参数的方法并且其返回类型TResut的委托。
- Action<T>:此委托封装一个方法,该方法只有一个参数并且不返回值。所以在使用 Action<T> 委托时,不必显式定义一个封装只有一个参数的方法(并且不能返回值)的委托。
所以再对上面的Filter进行改进:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace 委托Test
{
//delegate bool FilterDelegate(int i);
class Program
{
static void Main(string[] args)
{
int[] array = { , , , , , , , , };
//使用匿名方法来求偶数
//List<int> newList = MyFilter(array, delegate(int i) { // return i % 2 == 0;
//});
//使用Lambda表达式求偶数
List<int> newList = MyFilter(array, i => i % == ); foreach (int item in newList)
{
Console.WriteLine(item);
}
Console.ReadKey(); }
//Func<int,bool>: 封装了一个具有一个int参数并且返回类型为bool类型的方法
static List<int> MyFilter(int[] array,Func<int,bool> filter)
{
List<int> list = new List<int>();
for (int i = ; i < array.Length; i++)
{
if (filter(i))
{
list.Add(i);
}
}
return list;
}
}
}

回顾,A Simple Lambda Demo
- 下面Demo首先申明 Func<T, TResult> 变量,并为其分配了一个 lambda 表达式。
- 随后将封装此方法的委托(看下面实例)传递给Enumerable.Where、Enumerable.Order、 Enumerable.Select 方法,以将字符串数组中的字符串进行处理。
- ForEach 和 ForEach<T> 方法都采用 Action<T> 委托作为参数。 通过使用由委托封装的方法,可以对数组或列表中的每个元素执行操作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LambdaDemo
{
class Program
{
static void Main(string[] args)
{ string[] names = {"Eyes","Voodoo","Tod","Chris","Christina","Maxisim" };
Func<string, bool> filter = s => s.Length > ;
Func<string, string> order = s => s;
Func<string, string> operating = s => s.ToUpper(); IEnumerable<string> expr = names.Where(filter).OrderByDescending(order).Select(operating);
expr.ToList<string>().ForEach(i => Console.WriteLine(i));
Console.ReadKey(); }
}
}

Lambda表达式树
- 表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算。
- 并且你可以编译和运行由表达式树所表示的代码。这样的优势就是表达式树可以在运行的时候编译运行,而且可以对lambda表达式进行动态修改。
- 若要使用 API 创建表达式树,请使用 Expression 类。 此类包含创建特定类型的表达式树节点的静态工厂方法,例如,ParameterExpression(表示一个变量或参数),ConstantExpression(表示一个常量),MethodCallExpression(表示一个方法调用)。 ParameterExpression 、MethodCallExpression、ConstantExpression 以及其他表达式特定的类型也在 System.Linq.Expressions 命名空间中定义。 这些类型派生自抽象类型 Expression。
例如将表达式(Price-5)*Count*Rebate表示成一棵二叉树可以用以下方式表达:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions; namespace Lambda表达式树
{
class Program
{
static void Main(string[] args)
{
//计算(Price-5)*Count*Rebate
ParameterExpression paraPrice = Expression.Parameter(typeof(decimal),"price");
ConstantExpression constant = Expression.Constant(5m,typeof(decimal));
BinaryExpression result1 = Expression.Subtract(paraPrice, constant); ParameterExpression paraCount = Expression.Parameter(typeof(decimal),"count");
ParameterExpression paraRebate = Expression.Parameter(typeof(decimal),"rebate");
BinaryExpression result2 = Expression.Multiply(paraCount,paraRebate); BinaryExpression result3 = Expression.Multiply(result1,result2);
Expression<Func<decimal, decimal, decimal, decimal>> totalPrice = Expression.Lambda<Func<decimal, decimal, decimal, decimal>>(result3,paraPrice,paraCount,paraRebate);
Func<decimal, decimal, decimal, decimal> myFun = totalPrice.Compile();
Console.WriteLine(myFun(125m,10m,0.5m));
Console.ReadKey(); } }
}

分析表达式树
Expression<TDelegate> 类型提供 Compile 方法,该方法将表达式树表示的代码编译成一个可执行委托。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions; namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{ Expression<Func<int, int>> f1 = x => x + ;
//f1(1)//...错误,必须将表达式树表示的代码编译成一个可执行委托
Func<int, int> f2 = f1.Compile();
Console.WriteLine(f2());
Console.ReadKey();
}
}
}

文章来源:http://www.cnblogs.com/OceanEyes/archive/2012/08/27/2658920.html
Linq快速入门——Lambda表达式的前世今生的更多相关文章
- java8 快速入门 lambda表达式 Java8 lambda表达式10个示例
本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...
- 关于Linq中的Lambda表达式中OrderBy的深入理解
起因:就是一段Linq语句,OrderBy里面的i是什么? IQueryable<Student> slist = (from s in EFDB.Student select s). O ...
- Linq To Sql中实现Left Join与Inner Join使用Linq语法与lambda表达式
当前有两个表,sgroup与sgroupuser,两者通过gKey关联,而sgroup表记录的是组,而sgroupuser记录是组中的用户,因此在sgroupuser中不一定有数据.需要使用Left ...
- Linq专题之Lambda表达式
这一节我们讲的Lambda表达式跟匿名函数有关.Lambda表达式就是一个匿名函数,它可以包含表达式和语句,并且可以创建委托和表达式树. Lambda表达式的组成: 输入参数.Lambda运算符(=& ...
- 快速了解Lambda表达式-Java
目录 lambda表达式 前言 简介 简单入门 用法 好处 总结 lambda表达式 前言 最近因为疫情,也不能正常返校什么的,希望大家都能好好的,希望武汉加油,中国加油,在家也看了很多视频,学了一点 ...
- 3分钟入门lambda表达式
本节是lambda表达式的一个入门课,讲解的同时配有练习demo 前言什么是lambda表达式?基础语法函数式接口自己实现一个函数式接口jdk提供的函数式接口Consumersupplierfunct ...
- Lambda表达式的前世今生
Lambda 表达式 早在 C# 1.0 时,C#中就引入了委托(delegate)类型的概念.通过使用这个类型,我们可以将函数作为参数进行传递.在某种意义上,委托可理解为一种托管的强类型的函数指针. ...
- Linq快速入门——扩展方法
Linq为我们提供了许多扩展方法,方便我们对数据源进行操作(Where,Select...).即使你不了解算法,也能使用Linq当回牛人.扩展方法本质并不是什么高深的技术,说白了就是一个Static静 ...
- LINQ to Objects系列(3)深入理解Lambda表达式
Lambda表达式是学好LINQ很重要的一个知识点,后面的LINQ查询中会大量地使用到Lambda表达式.这篇文章从以下几点进行总结. 1,Lambda表达式的前世今生 2,Lambda表达式的实际运 ...
随机推荐
- jconsole工具使用----jvm内存泄漏问题
转载地址:https://www.cnblogs.com/baihuitestsoftware/articles/6405580.html Jconsole,Java Monitoring and M ...
- spring boot 学习(四)Druid连接池的使用配置
Druid介绍 Druid是一个JDBC组件,druid 是阿里开源在 github 上面的数据库连接池,它包括三部分: * DruidDriver 代理Driver,能够提供基于Filter-Cha ...
- poj3481
题解: splay操作 读入速度太慢,导致超时... 用字符串gets操作 代码: #pragma GCC optimize(2) #include<cstdio> #include< ...
- python 加密 解密 签名 验证签名 公钥 私钥 非对称加密 RSA
加密与解密,这很重要,我们已经可以用pypcap+npcap简单嗅探到网络中的数据包了.而这个工具其实可以嗅探到更多的数据.如果我们和别人之间传输的数据被别人嗅探到,那么信息泄漏,信息被篡改,将给我们 ...
- docker(三)反正我不喜欢敲命令,daocloud.io管理你的docker,安装远程下载工具aria2 迅雷远程下载 xware
1.登录daocloud.io 2.寻找合适的镜像 3.查看镜像信息并部署,它给出了一个运行命令,其中有用的只是-V,目录映射,映射了物理服务器的/tddownload到容器的/xware/tddow ...
- linux驱动程序:控制发光二极管
一个完整的Linux驱动包括内部处理和交互两部分.其中内部处理主要是指Linux驱动的装载.卸载.与设备文件的相关动作处理以及业务逻辑等.与硬件交互主要是指通过iowrite32.ioread32 ...
- 关于如何使用cg中的discard/clip
Pass { ZTest Always Cull Off ZWrite Off Fog { Mode off } Blend Off CGPROGRAM #pragma vertex vert_img ...
- Eclipse js报错问题解决办法
最近在Eclipse中导入新项目后会发现js报错,但是不影响程序的运行,但是对于程序员的我们来说多少还是比较在意代码前面的红色的X的,有木有??? 上网也查了很多方法,对于其中一种方法表示不能完全解决 ...
- 接下来将介绍C#如何设置子窗体在主窗体中居中显示,本文提供详细的操作步骤,需要的朋友可以参考下
接下来将介绍C#如何设置子窗体在主窗体中居中显示,本文提供详细的操作步骤,需要的朋友可以参考下 其实表面上看是很简单的 开始吧,现在有两个窗体Form1主窗体,Form2子窗体 而且我相信大部分人都会 ...
- HandBrake 开源视频转码器、编码转换器、格式转换器
HandBrake 开源视频转码器.编码转换器.格式转换器 点击下图进入官网下载页面:https://handbrake.fr/downloads.php macOS 下可能会阻止安装! 其实也不是安 ...