什么是LambdaExpression,如何转换成Func或Action(2)
在上一篇中,我们认识了什么是表达式树、什么是委托,以及它们的关系。可能是我功力不好,貌似大家都不怎么关注,没有讲解出不同角度的问题。
学习一种新技术,是枯燥的过程,只有在你掌握后并能运用时才能从它身上得到乐趣。
做程序开发是一群很奇怪的人群,我们居然可以通过密密麻麻的英文字符加上标点符号从中找到乐趣,确实很奇怪。
考虑到大家接触新技术需要一个过程:
其实对于很多人来说已经不是新技术了,不过您会耐心看本篇后续的文章,说明您可能对这一项技术运用的并不是很熟练
所以我不打算一上来,就放一大堆代码,然后解释这是什么,那是什么,因为会接触很多新的关键词,这样大家学习起来会也会很痛苦
本系列后面的所有篇幅都只在每篇中提一个新概念,这样大家学习起来可以减少学习的范围。
然后也让大家彻底搞懂每种类型的作用,同时我会用Lambda方式、动态构造、以及表达式树的结构三种方式来共同研究每篇课题的新类型。
LambdaExpression是继承自Expression的。LambdaExpression的具体表现是:Expression<Func<>>或者Expression<Action<>>
这段不明白没关系,看下面示例就知道了
首先,我们先从MSDN上看它的注释说明:
描述一个 lambda 表达式。 这将捕获与 .NET 方法体类似的代码块。
看MSDN注释,我们还是没搞懂它是什么意思,通俗的讲:一段包含有运算表达式的代码,就是LambdaExpression。
好吧,我说了吧,我的功底不行,您越看越不明白了………………(比MSDN解释的还更糟糕)
Expression<Func<int,int>> exp1 = o=> o + ;
Expression<Action<int>> exp2 = o=> o = + ;
这种通过 o=> ...... 定义的就是LambdaExpression了。回过头,我上面说的:
一段包含有运算表达式的代码,就是LambdaExpression。
这样子是不是更容易理解了?当然上面只做了 加法操作,当然不仅仅是这些操作,比如:
Expression<Func<UserVO, object>> exp = o => o.ID != && (o.UserName == "steden" || o.UserName == "farseer.net")
再比如在我们的Linq To Object中(当然这里是纯委托类型:Func<int,bool>,但它也是lambda表达式(注意不是表达式树))
var lst = new List<int>();
lst.Where(o => o > );
这些都是Lambda的定义。并且我们在上篇中也学习到如何将Expression<Func<UserVO,object>>转换成Func<UserVO,object>。
这时候聪明的读者会想,即然我可以直接定义o=>.... ,为什么还要去理解LambdaExpression,反正C#编译器会把它转成LambdaExpression,根本不用我们关心。
确实是这样,如果我们不需要动态构造它
我的意思是在程序运行时动态的生成它,则不是在编写代码的时候定义它
的时候,确实不用管是不是LambdaExpression了,只管在代码上定义就行了。
但是,其实很多场景下,我们是需要动态的构造它的,然后将它传递给其它地方,让他们去解析它,比如说:
场景:在系统分层中,我们有个实体类,比如叫:UserVO(它属于xxx.Entity类库)。而在我们的底层中,需要动态对实体类生成一些“通用的”操作(比如逻辑删除功能)。但是我们知道底层是不知道上层有什么数据类型,甚至被谁调用了也不知道。因此这个时候,我就必须以动态构造的方式来创建它了。(因为我根本不知道有UserVO这个类)
事实上,上面举的场景例子不仅仅是LambdaExpression,其它的Expression也是如此。
在讲动态构造前,我觉得还是先让大家学习如何解析它,必境我们的学习是先了解它的内部结构,才更好的知道如何构造它,不是吗?
这要请出我们伟大的ExpressionVisitor类了。事实上,在我的Farseer.Net ORM框架中重新封装了这个类,叫AbsExpressionVisitor.cs,它是我所有表达式树访问器的基类,另外派出了一个专门提供给SQL的解析器,叫AbsSqlVisitor.cs
其中有个入口方法:
protected virtual Expression Visit(Expression exp)
ExpressionVisitor以访问者模式(设计模式)来访问这个表达式树。可以看到有个:exp.NodeType属性,返回的是:ExpressionType枚举:
protected virtual Expression Visit(Expression exp)
{
if (exp == null)
return exp;
switch (exp.NodeType)
{
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return this.VisitUnary((UnaryExpression)exp);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return this.VisitBinary((BinaryExpression)exp);
case ExpressionType.TypeIs:
return this.VisitTypeIs((TypeBinaryExpression)exp);
case ExpressionType.Conditional:
return this.VisitConditional((ConditionalExpression)exp);
case ExpressionType.Constant:
return this.VisitConstant((ConstantExpression)exp);
case ExpressionType.Parameter:
return this.VisitParameter((ParameterExpression)exp);
case ExpressionType.MemberAccess:
return this.VisitMemberAccess((MemberExpression)exp);
case ExpressionType.Call:
return this.VisitMethodCall((MethodCallExpression)exp);
case ExpressionType.Lambda:
return this.VisitLambda((LambdaExpression)exp);
case ExpressionType.New:
return this.VisitNew((NewExpression)exp);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return this.VisitNewArray((NewArrayExpression)exp);
case ExpressionType.Invoke:
return this.VisitInvocation((InvocationExpression)exp);
case ExpressionType.MemberInit:
return this.VisitMemberInit((MemberInitExpression)exp);
case ExpressionType.ListInit:
return this.VisitListInit((ListInitExpression)exp);
default:
throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
}
然后根据传入进来的表达式树,进行一一解析,您能从中看到,当case 到 ExpressionType.Lambda时,会强制转换成LambdaExpression
并传入VisitLambda方法中:
protected virtual Expression VisitLambda(LambdaExpression lambda)
{
Expression body = this.Visit(lambda.Body);
if (body != lambda.Body)
{
return Expression.Lambda(lambda.Type, body, lambda.Parameters);
}
return lambda;
}
事实上,这段代码不用太过理解其它部份,只需要知道:
当Expression的NodeType == ExpressionType.Lambda时,是可以显示转换成:LambdaExpression的。
并且它有一个叫Body的属性:(获取 lambda 表达式的主体。),以及一个叫Parameters的属性:(获取 lambda 表达式的参数。)
Body返回的是LambdaExpression的主体。
主体:指在LambdaExpression中的主要结构,或者说主要表达式。比如红色标记部份的:
o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")
而Parameters的返回的是参数表达式树(出现了一个新名词,下篇会详细讲解)。这里简单的讲解:上面出现的o 即是参数,o的类型是UserVO
接着上面的:VisitLambda方法里继续访问:this.Visit(lambda.Body),也就是解析主体部份。
从上面的表达式代码(红色部份),它会执行:this.VisitBinary((BinaryExpression)exp);方法。
在这里知道BinaryExpression(表示包含二元运算符的表达式。)是对于上面的&&的解析就足够了。
在上篇我们强调了Expression是一种数据结构,也就是说红色部份,我们定义的代码实质是被保存成一种数据结构的,如图:

红色底是:BinaryExpression类型(表示包含二元运算符的表达式。)二元运算,比如 && || >= < !=
蓝色底是:ParameterExpression类型(表示命名的参数表达式。)传入的参数,比如UserVO实体类
绿色底是:ConstantExpression类型(表示具有常量值的表达式。)具体的常量值。
由于太晚了,这篇末尾的动态构造,放到下一篇中
什么是LambdaExpression,如何转换成Func或Action(2)的更多相关文章
- Epplus下的一个将Excel转换成List的范型帮助类
因为前一段时间公司做项目的时候,用到了Excel导入和导出,然后自己找了个插件Epplus进行操作,自己将当时的一些代码抽离出来写了一个帮助类. 因为帮助类是在Epplus基础之上写的,项目需要引用E ...
- DataTable转换成IList<T>的简单实现
DataTable的无奈 很多时候,我们需要去操作DataTable.但DataTable的操作,实在是太不方便了.Linq?lambda表达式?统统没有... 特别是对现有结果集做进一步筛选,这样的 ...
- 利用TaskCompletionSource将EAP转换成TAP
1.原始的异步方法的调用 我们来看个简单的例子,在这里演示调用 WebClient.DownloadStringAsync 方法(这个方法不是 TAP),然后由 WebClient.Dow ...
- nodejs将PDF文件转换成txt文本,并利用python处理转换后的文本文件
目前公司Web服务端的开发是用Nodejs,所以开发功能的话首先使用Nodejs,这也是为什么不直接用python转换的原因. 由于node对文本的处理(提取所需信息)的能力不强,类似于npm上的包: ...
- (转) 将VB.NET网站转换成C#的全过程
在学习URL重写过程中碰到个是VB写的源码,看起来总是不爽的就GOOLE了下 感觉这个文章写的不错 原文地址 http://www.cnblogs.com/cngunner/archive/2006/ ...
- 工作随笔——Golang interface 转换成其他类型
新的公司,新的氛围.一年了,打算写点什么.so,那就写google的golang语言吧. 最最最基础的语法结构见go语言菜鸟教程 接下来写点菜鸟教程没有的. go语言的设计者认为:go语言必须让程序员 ...
- [Swift]LeetCode709. 转换成小写字母 | To Lower Case
Implement function ToLowerCase() that has a string parameter str, and returns the same string in low ...
- Spring MVC 后端获取前端提交的json格式字符串并直接转换成control方法对应的参数对象
场景: 在web应用开发中,spring mvc凭借出现的性能和良好的可扩展性,导致使用日渐增多,成为事实标准,在日常的开发过程中,有一个很常见的场景:即前端通过ajax提交方式,提交参数为一个jso ...
- Swift3 根据秒数获取视频时长(转换成00:00:00时间格式)以及将时长转换成秒
直接代码了: /// 秒转换成00:00:00格式 /// /// - Parameter secounds: <#secounds description#> /// - Returns ...
随机推荐
- form标签中id和name属性的区别
HTML元素的ID和Name属性的区别 一直认为ID和NAME是一样的,两个又可以一起出现,甚是疑惑. 今天BAIDU了一下,才发现里面大有文章.发出来研究研究: 最classical的答案:ID就像 ...
- (6)servlet-创建一个servlet类
1,在项目的src目录下,右键[New]-[Servlet] 2,在弹出窗口中填写信息 Package:所在包名 Name:servlet的类名 Which method stubs would yo ...
- Linux下高并发socket最大连接数所受的各种限制(详解)
1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每 ...
- [IMX6DL][Android4.4] 电池低电量告警提示【转】
本文转载自:http://blog.csdn.net/kris_fei/article/details/51789964 之前版本的电池电量低是通过发送 intent ACTION_BATTERY_L ...
- yii2.0 ActiveRecord 查询汇总
User::find()->all(); 此方法返回所有数据: User::findOne($id); 此方法返回 主键 id=1 的一条数据(举个例子): User::find()->w ...
- AutoIT: ControlSetText
1. ControlSetText :可以摆脱Send的限制,在适当的文本框位置输入用户想要输入的信息.2. ControlGetText可以获取文本 Run("notepad.exe&qu ...
- RobotFrameWork--selenium2模拟chrome的user agent
${options}= Evaluate sys.modules['selenium.webdriver'].ChromeOptions() sys, selenium.webdriver ${opt ...
- 《Image Generation with PixelCNN Decoders》论文笔记
论文背景:Google Deepmind团队于2016发表在NIPS上的文章 motivation:提出新的image generation model based on pixelCNN[1]架构. ...
- Ubuntu 16.04安装Wireshark进行抓包
技巧: 1.可以通过tcpdump抓取某个网卡的包,然后输出日志文件,通过Wireshark进行分析. 2.可以设置Wifi热点,然后通过手机连接这个热点,然后进行tcpdump的分析,然后输出日志文 ...
- Entity Framework 学习整理
MSDN: http://msdn.microsoft.com/en-us/data/aa937723 台湾博客: http://www.dotblogs.com.tw/yc421206/ http: ...