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

序言

  在上一篇中,咱们认识了什么是表达式树、什么是委托,以及它们的关系。多是我功力很差,貌似你们都不怎么关注,没有讲解出不一样角度的问题。html

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

  作程序开发是一群很奇怪的人群,咱们竟然能够经过密密麻麻的英文字符加上标点符号从中找到乐趣,确实很奇怪。github

  考虑到你们接触新技术须要一个过程:express

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

  因此我不打算一上来,就放一大堆代码,而后解释这是什么,那是什么,由于会接触不少新的关键词,这样你们学习起来会也会很痛苦数据结构

  本系列后面的全部篇幅都只在每篇中提一个新概念,这样你们学习起来能够减小学习的范围。框架

  而后也让你们完全搞懂每种类型的做用,同时我会用Lambda方式、动态构造、以及表达式树的结构三种方式来共同研究每篇课题的新类型。ide

什么是LambdaExpression

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

这段不明白不要紧,看下面示例就知道了this

  首先,咱们先从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类型(表示具备常量值的表达式。)具体的常量值。

总结

因为太晚了,这篇末尾的动态构造,放到下一篇中

相关文章
相关标签/搜索