转载

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

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

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

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

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

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

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

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

然后也让大家彻底搞懂每种类型的作用,同时我会用 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框架 中重新封装了这个类(目前还没有发布上来,在 V1.2 后发布,后面我会贴上地址来)

其中有个入口方法:

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是一种数据结构 ,也就是说红色部份,我们定义的代码实质是被保存成一种 数据结构 的,如图:

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

红色底是: BinaryExpression 类型(表示包含二元运算符的表达式。)二元运算,比如 && || >= < !=

蓝色底是: ParameterExpression 类型(表示命名的参数表达式。)传入的参数,比如UserVO实体类

绿色底是: ConstantExpression 类型(表示具有常量值的表达式。)具体的常量值。

LambdaExpression的构造

时间太晚了,留在白天在继续完善。

总结

时间太晚了,留在白天在继续完善。

正文到此结束
Loading...