序言

  在上一篇中,我们认识了什么是表达式树、什么是委托,以及它们的关系。可能是我功力不好,貌似大家都不怎么关注,没有讲解出不同角度的问题。

  学习一种新技术,是枯燥的过程,只有在你掌握后并能运用时才能从它身上得到乐趣。

  做程序开发是一群很奇怪的人群,我们居然可以通过密密麻麻的英文字符加上标点符号从中找到乐趣,确实很奇怪。

  考虑到大家接触新技术需要一个过程:

其实对于很多人来说已经不是新技术了,不过您会耐心看本篇后续的文章,说明您可能对这一项技术运用的并不是很熟练

  所以我不打算一上来,就放一大堆代码,然后解释这是什么,那是什么,因为会接触很多新的关键词,这样大家学习起来会也会很痛苦

  本系列后面的所有篇幅都只在每篇中提一个新概念,这样大家学习起来可以减少学习的范围。

  然后也让大家彻底搞懂每种类型的作用,同时我会用Lambda方式、动态构造、以及表达式树的结构三种方式来共同研究每篇课题的新类型。

什么是LambdaExpression

  LambdaExpression是继承自Expression的。LambdaExpression的具体表现是:Expression<Func<>>或者Expression<Action<>>

这段不明白没关系,看下面示例就知道了

  首先,我们先从MSDN上看它的注释说明:

描述一个 lambda 表达式。 这将捕获与 .NET 方法体类似的代码块。

  看MSDN注释,我们还是没搞懂它是什么意思,通俗的讲:一段包含有运算表达式的代码,就是LambdaExpression。

  好吧,我说了吧,我的功底不行,您越看越不明白了………………(比MSDN解释的还更糟糕)

LambdaExpression的定义
Expression<Func<int,int>> exp1 = o=> o + 1; Expression<Action<int>> exp2 = o=> o = 1 + 2;

  这种通过 o=> ...... 定义的就是LambdaExpression了。回过头,我上面说的:

一段包含有运算表达式的代码,就是LambdaExpression。

  这样子是不是更容易理解了?当然上面只做了 加法操作,当然不仅仅是这些操作,比如:

Expression<Func<UserVO, object>> exp = o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")

  再比如在我们的Linq To Object中(当然这里是纯委托类型:Func<int,bool>,但它也是lambda表达式(注意不是表达式树))

var lst = new List<int>();
lst.Where(o => o > 0);

  这些都是Lambda的定义。并且我们在上篇中也学习到如何将Expression<Func<UserVO,object>>转换成Func<UserVO,object>。

LambdaExpression有什么用?

  这时候聪明的读者会想,即然我可以直接定义o=>.... ,为什么还要去理解LambdaExpression,反正C#编译器会把它转成LambdaExpression,根本不用我们关心。

  确实是这样,如果我们不需要动态构造它

我的意思是在程序运行时动态的生成它,则不是在编写代码的时候定义它

  的时候,确实不用管是不是LambdaExpression了,只管在代码上定义就行了。

  但是,其实很多场景下,我们是需要动态的构造它的,然后将它传递给其它地方,让他们去解析它,比如说:

场景:在系统分层中,我们有个实体类,比如叫:UserVO(它属于xxx.Entity类库)。而在我们的底层中,需要动态对实体类生成一些“通用的”操作(比如逻辑删除功能)。但是我们知道底层是不知道上层有什么数据类型,甚至被谁调用了也不知道。因此这个时候,我就必须以动态构造的方式来创建它了。(因为我根本不知道有UserVO这个类)

  事实上,上面举的场景例子不仅仅是LambdaExpression,其它的Expression也是如此。

  在讲动态构造前,我觉得还是先让大家学习如何解析它,必境我们的学习是先了解它的内部结构,才更好的知道如何构造它,不是吗?

LambdaExpression的解析

  这要请出我们伟大的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类型(表示具有常量值的表达式。)具体的常量值。

转载于:https://www.cnblogs.com/kk-home/p/9249643.html

什么是LambdaExpression,如何转换成Func或Action(2)相关推荐

  1. 利用TaskCompletionSource将EAP转换成TAP

    1.原始的异步方法的调用 我们来看个简单的例子,在这里演示调用 WebClient.DownloadStringAsync 方法(这个方法不是 TAP),然后由 WebClient.DownloadS ...

  2. golang 开发 Struct 转换成 map 两种方式比较

    原文链接:https://www.jianshu.com/p/81c4304f6d1b 最近做Go开发的时候接触到了一个新的orm第三方框架gorose,在使用的过程中,发现没有类似beego进行直接 ...

  3. 技巧:Go 结构体如何转换成 map[string]interface{}

    本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的"坑",也有你需要知道的若干方法. 我们在Go语言中通常使用结构体来保存我们的数据,例如要 ...

  4. Go开发Struct转换成map两种方式比较

    最近做Go开发的时候接触到了一个新的orm第三方框架gorose,在使用的过程中,发现没有类似beego进行直接对struct结构进行操作的方法,有部分API是通过map进行数据库相关操作,那么就需要 ...

  5. 如何将自定义的 Delegate 转成 Func 委托?

    咨询区 AndreyAkinshin 场景是这样的,我自定义了一个 SomeDelegate 委托,然后将 Inc 方法灌入到其中,同时我也将 Inc 赋值给了 Func<int,int> ...

  6. python list转换成array_一文掌握Python【不定期更新】

    目录 一.Numpy 1 基本操作 2 随机数 3 打乱训练数据 4 得到元素的最值 5 拼接数组 6 得到函数的信息 7 得到累乘即各项相乘的结果 8 判断一个数是否在数组中 9 数组的变换 10 ...

  7. 属性拼接转换成字符串_使用一些可选的将字符串配置属性转换为其他类型

    属性拼接转换成字符串 有一天,您遇到一些代码,并认为这很漂亮,为什么我没有想到呢? 因此,我的长期同事Mark Warner在使用方法引用处理从String进行转换的标准名称/值存储模式方面有一个不错 ...

  8. 709. 转换成小写字母 golang 字符串处理

    题目 转换成小写字母 实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串. 示例 1: 输入: "Hello& ...

  9. 二叉树题目----6 二叉树的最近公共祖先 AND 二叉树搜索树转换成排序双向链表

    二叉树的最近公共祖先 思路 在左.右子树中分别查找是否包含p或q: 如果以下两种情况(左子树包含p,右子树包含q/左子树包含q,右子树包含p),那么此时的根节点就是最近公共祖先 如果左子树包含p和q, ...

最新文章

  1. [Ubuntu] SVN常用的批量操作
  2. Euro Truck Simulator 2欧洲卡车模拟2用VR玩
  3. 理清Python网络编程
  4. 字符串与指针,数组的关系与用途
  5. 金算盘高手论坛资料中心_3D304期 菜鸟论坛精英PK专栏 速来围观!!
  6. 图匠数据等提出高精度零售货架姿态估计算法GSPN
  7. iPhone判断是否已插入SIM卡
  8. jvm最大最小内存参数设置
  9. 真正支配整个世界的十种算法
  10. PHP经典面试题汇总(续)
  11. 【java笔记】Iterator迭代器 增强for
  12. 平板电脑开机出现android,平板电脑常见故障解决方法
  13. python对Excel合并单元格拆分
  14. CentOS6.6下配置KMS自动激活服务器
  15. noip赛后【微笑】
  16. 华为数通HCIA学习笔记之OSI参考模型TCP/IP模型
  17. jenkins编译打包及自动化部署
  18. 对于算法工程师职业生涯规划的考虑
  19. 729. 我的日程安排表 I
  20. UnicodeDecodeError: 'gbk' codec can't decode byte 0x91 in position 8: illegal multibyte sequence

热门文章

  1. 高并发下的static类成员可能存在安全隐患
  2. 排序箭头,升序,降序简单实现
  3. 微软发布紧急更新:修复Flash高危漏洞
  4. Java知多少(15)字符串
  5. Ext的viewport在所有浏览器下出现界面内容消失的问题
  6. 一个简单的apache cgi-bin
  7. 时间管理神器:滴答清单之我最喜欢的特征
  8. 宅家学习秘密武器!让时间从视频流和朋友圈中回到程序员的视野中吧!
  9. 剑桥的商学院硕士的创业项目找我做tech lead,稳得一批
  10. 1.7 Appium依赖添加