大家好,由于今天项目升级,大家都在获最新代码,所以我又有时间在这里写点东西,跟大家分享。

在上一篇的文章中我介绍了一个dll,使大家在debug的时候可以可视化的看到ExpressionTree的Body和Parameter。今天这篇文章主要讲一个问题——如何利用一个已有的表达式树的body来构建一个新的表达式树。      不多说废话,现在开始进入正题。

假设我们要写一个像下面这样的方法:

 public Func<string, bool> ReBuildExpression(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)

这个方法的用意很简单,就是把传入的两个参数为string类型,返回类型为bool的方法做一个且的关系,构建出一个新的以string为参数,返回类型为bool的方法。

举个例子,如果传入的lambd0,lambd1为如下谓语表达式:

 Expression<Func<string, bool>> lambda0 = item => item.Length > 2;
            Expression<Func<string, bool>> lambda1 = item => item.Length < 4;

那么,我们希望ReBuildExpression这个方法返回的谓语表达式为item=>item.Length>2&&item.Length<4。

通过上一篇的介绍我们知道,对于一个表达式树来说,我们可以把它分为2部分——body和parameter,逻辑在body,参数在parameter,那么很自然的,我们想到采用如下方式来实现这个方法:


        public Func<string, bool> ReBuildExpression(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)
        {
            parameter = Expression.Parameter(typeof(string), "name");
            Expression left = lambd0.Body;
            Expression right = lambd1.Body;
            BinaryExpression expression = Expression.AndAlso(left, right);
            Expression<Func<string, bool>> lambda = Expression.Lambda<Func<string, bool>>(expression, parameter);
            return lambda.Compile();
        }

  相信通过前几篇的介绍大家已经能大致看懂这段程序了,这段程序只是把传入的两个表达式树做了一个且的关系,构建出一个新的谓语表达式并传出。

那么,这段程序的运行结果如何呢?

大家如果调用这个方法,运行程序就会发现程序会抛出一个异常:Lambda Parameter not in scope。

这个异常字面的意思是Lambda参数不在作用范围内,为什么会有这个异常呢?

下面,请允许我用一个例子来解释这个问题。

我们将一个表达式树比作一辆2轮自行车,那么body就是自行车骨架,parameter就是2个车轮。

好了,我们可以把上面代码中的lambd0和lambd1看成2辆双轮自行车。

我们在代码中想把这2辆双轮自行车拼接成一辆3人骑的4轮自行车,所以我们写了以下代码:

Expression left = lambd0.Body;
      Expression right = lambd1.Body;

注意!这就是问题的关键所在,这里我们只是引用了这2个自行车的骨架,而不是复制!我们希望构造出的4轮自行车没有任何骨架,这2句只是说想引用已有的2个骨架,但问题就来了,已有的自行车骨架还连接着lambd0和lambd1的车轮,并不能被新的自行车所用,我们必须按照已有的骨架复制出一个一模一样的骨架才能被我们的新的4轮自行车所用。

在这里截个lambda0的图给大家看:

  

这是用上一篇的工具看到的,大家注意看红色框框中的部分,Body中的memberExpression是记录了parameter的信息的,这就是问题所在。

所以就有了下面的解决方案。

首先,我们要在项目中加入一个新的文件——ExpressionVisitor.cs,这个文件是上一篇的dll中的一个源文件./Files/FlyEdward/ExpressionVisitor.zip

大家可以从上面的链接中把它下载下来。我在这里粘贴这个类的声明给大家看看


 public abstract class ExpressionVisitor
    {
        protected ExpressionVisitor()
        {
        }         protected virtual Expression Visit(Expression exp)
        {

大家在这里可以看到这个类是个抽象类,然后Visit的访问权限也是protected。

所以,我们必须再自己实现一个类,并且暴露出一个public的类来调用这个Visit方法。

这里解释一下,ExpressionVisitor这个类的设计初衷是修改表达式树而并非复制表达式树,所以才把类设计成了abstract的,并且visit还是protected的,目的就是要用户自己实现一个子类,定义修改的规则,下面的链接是一个例子,把一个表达式树中的所有“且”逻辑修改成“或”逻辑。

http://msdn.microsoft.com/zh-cn/library/bb546136.aspx
     好了,接着我们的项目说,我们自己实现一个子类来调用visit方法来访问并复制表达式树。


   public class ExpressionVisitorMy : ExpressionVisitor
    {
        public ParameterExpression Parameter
        {
            get;
            set;
        }         public System.Linq.Expressions.Expression Modify(System.Linq.Expressions.Expression exp)
        {
            return this.Visit(exp);
        }         protected override Expression VisitParameter(ParameterExpression p)
        {
            return Parameter;
        }
    }

大家注意,在这个类里我们新增了一个Parameter属性,我们可以把新的Parameter赋给这个属性,而不要它去访问以前的“车轮”。

好了,接着,就可以实现之前的那个方法了:


        public Func<string, bool> ReBuildExpression(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)
        {
            ExpressionVisitorMy visitor = new ExpressionVisitorMy();
            ParameterExpression parameter = Expression.Parameter(typeof(string), "name");
            visitor.Parameter = parameter;
            Expression left = visitor.Modify(lambd0.Body);
            Expression right = visitor.Modify(lambd1.Body);
            BinaryExpression expression = Expression.AndAlso(left, right);
            Expression<Func<string, bool>> lambda = Expression.Lambda<Func<string, bool>>(expression, parameter);
            return lambda.Compile();
        }

  大家注意:

  ParameterExpression parameter = Expression.Parameter(typeof(string), "name");
      visitor.Parameter = parameter;
      这2句只是表示这个表达式树对应的参数类型为string,形参的名字是name。

好,下面贴出Program中的完整代码:


class Program 
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string> { "Cai", "Edward", "Beauty" };              Expression<Func<string, bool>> lambda0 = item => item.Length > 2;
            Expression<Func<string, bool>> lambda1 = item => item.Length < 4;
            Expression<Func<string, bool>> lambda2 = name => name.Length > 2 && name.Length < 3;             Program program = new Program();
            Func<string, bool> method = program.ReBuildExpression(lambda0, lambda1);             
            var query = names.Where(method);             foreach (string n in query)
            {
                Console.WriteLine(n);
            }
            Console.Read();
        }         public Func<string, bool> ReBuildExpression(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)
        {
            ExpressionVisitorMy visitor = new ExpressionVisitorMy();
            ParameterExpression parameter = Expression.Parameter(typeof(string), "name");
            visitor.Parameter = parameter;
            Expression left = visitor.Modify(lambd0.Body);
            Expression right = visitor.Modify(lambd1.Body);
            BinaryExpression expression = Expression.AndAlso(left, right);
            Expression<Func<string, bool>> lambda = Expression.Lambda<Func<string, bool>>(expression, parameter);
            return lambda.Compile();
        }
             }

这段程序的运行结果就是输出长度大于2,小于4的"Cai"。

   今天就跟大家分享到这里,下一篇中我将结合自己开发中遇到的问题,跟大家举例讲解一些表达式树的应用场景和今天讲的ExpressionVisitor的应用场景。希望大家能继续关注。

Linq系列(7)——表达式树之ExpressionVisitor的更多相关文章

  1. Linq系列(5)——表达式树之案例应用

    在进入今天的正题之前,先感慨下本人的blog的人气一篇不如一篇.再加上换公司后人身自由受到了比之前大得多得多的限制,实在令本人有些郁闷.不过每次提笔写些东西跟大家分享,总是能让我感到愉悦和欣慰,希望我 ...

  2. LINQ to Objects系列(4)表达式树

    为了进一步加深对Lambda表达式的理解,我们需要掌握一个新的知识,Lambda表达式树,可能听名字看起来很高深和难以理解,但实际上理解起来并没有想象中那么难,这篇文章我想分以下几点进行总结. 1,表 ...

  3. LINQ Expresstion Tree 表达式树

    Expression trees represent code in a tree-like data structure, where each node is an expression, for ...

  4. C#动态构建表达式树(三)——表达式的组合

    C#动态构建表达式树(三)--表达式的组合 前言 在筛选数据的过程中,可能会有这样的情况:有一些查询条件是公共的,但是根据具体的传入参数可能需要再额外增加一个条件.对于这种问题一般有两种方法: a. ...

  5. C# - LINQ 表达式树

    表达式树(Expression Tree) 表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression ...

  6. 追根溯源之Linq与表达式树

    一.什么是表达式树?   首先来看下官方定义(以下摘录自巨硬官方文档)   表达式树表示树状数据结构中的代码,其中每个节点都是表达式,例如,方法调用或诸如的二进制操作x < y.   您可以编译 ...

  7. C#3.0新增功能10 表达式树 03 支持表达式树的框架类型

    连载目录    [已更新最新开发文章,点击查看详细] 存在可与表达式树配合使用的 .NET Core framework 中的类的大型列表. 可以在 System.Linq.Expressions 查 ...

  8. [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门

    [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...

  9. Lambda表达式和Lambda表达式树

    LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态. 为了富有效率的使用数据库和其他查询引擎,我们需要一种不同的方式表示管道中的各个操作.即把代码当作可在编程中进行检查的数据. Lambd ...

随机推荐

  1. 当ASP.NET Forms验证方式遭遇苹果IOS

    一.问题出现 我在用ASP.NET MVC4做微信开发的时候,用Forms验证方式做为authentication. 一般都是在web.config加: <authentication mode ...

  2. 磁盘爆满导致MySQL无法启动:Disk is full writing './mysql-bin.~rec~' (Errcode: 28). Waiting for someone to free space...

    今天收到监控邮件说博客访问失败.打开页面一看,硕大的502 Bad Gateway,ping了一下VPS发现是通的,SSH连接上去看了下Nginx日志发现没问题,重启lnmp的时候发现Mysql起不来 ...

  3. ARP/RARP

    IP地址和MAC地址之间的转换   点对点链路不使用ARP   ARP高速缓存   ARP高效运行的关键是每个主机上都有一个ARP高速缓存. 存放了最近IP地址与MAC地址间的映射关系. 每一项生存时 ...

  4. 消除SDK更新时的“https://dl-ssl.google.com refused”错误

    消除SDK更新时,有可能会出现这样的错误: Download interrupted: hostname in certificate didn't match: <dl-ssl.google. ...

  5. 关于RBAC(Role-Base Access Control)的理解

    基于角色的访问控制(Role-Base Access Control) 有两种正在实践中使用的RBAC访问控制方式:隐式(模糊)的方式和显示(明确)的方式. 今天依旧有大量的软件应用是使用隐式的访问控 ...

  6. 介绍下Shell中的${}、##和%%使用范例

    假设定义了一个变量为:代码如下:file=/dir1/dir2/dir3/my.file.txt可以用${ }分别替换得到不同的值:${file#*/}:删掉第一个 / 及其左边的字符串:dir1/d ...

  7. Android学习(十八)Toast的使用

    一.什么是Toast 1.Toast是一种提供给用户简洁提示信息的视图. 2.该视图以浮于应用程序之上的形式呈现给用户, Toast提示界面不获取焦点,在不影响用户使用的情况下,给用户某些提示. 3. ...

  8. python——内置对象

    python的内置对象 对象类型 常量示例/用法 Number(数字) 3.14159, 1234, 999L 3+4j String(字符串) 'spam', "guido's" ...

  9. 阿里巴巴 DevOps 转型后的运维平台建设

    原文:http://www.sohu.com/a/156724220_262549 本文转载自公众号「DevOps 时代」,高效运维社区致力于陪伴您的职业生涯,与您一起愉快的成长. 作者简介: 陈喻( ...

  10. the reactor pattern and java nio

    在<java NIO>作者PPT<How to Build a Scalable Multiplexed Server With NIO> 和 Doug Lea <Sca ...