[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用
[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用
本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构。是时候来引用他解决问题。而本节主要目的就是使用表达式树解决实际问题。
读前必备:
本节学习前,需要掌握以下知识:
A.继承 [.net 面向对象编程基础] (12) 面向对象三大特性——继承
B.多态 [.net 面向对象编程基础] (13) 面向对象三大特性——多态
C.抽象类 [.net 面向对象编程基础] (15) 抽象类
D.泛型 [.net 面向对象编程基础] (18) 泛型
1.动态创建表达式树
上一节中通过对表达式树结构和解析表达式的学习以后,动态创建表达式树,已经变得非常简单了,下面我们使用表达式树动态创建下节的Lambda表达式.
先看我们要最终完成的原表达式:
Expression<Func<int, int, bool>> expression = (x, y) => x!=0 && x==y+1;
动态创建过程如下:
//动态创建表达式树 Expression<Func<int, int, bool>> expression = (x, y) => x != 0 && x == y + 1;//先创建两个参数 ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(typeof(int),"x"), Expression.Parameter(typeof(int), "y") };ParameterExpression param1 = parameters[0]; ParameterExpression param2 = parameters[1];//下面先创建右边第一个表达式 x!=0 //(1)常量 0x ConstantExpression rightLeftRight = Expression.Constant(0, typeof(int)); //(2)创建左边第一个表达式 x!=0 BinaryExpression rightLeft = Expression.NotEqual(param1, rightLeftRight);//下面创建右边第二个表达式 x==y+1 //(3) 先创建右边部分表达式y+1 BinaryExpression rightRightRight = Expression.Add(param2, Expression.Constant(1, typeof(int))); //(4)创建右边表达式 x==y+1 BinaryExpression rightRight = Expression.Equal(param1, rightRightRight);//5)创建表达式 右部整体 x != 0 && x == y + 1 BinaryExpression Right = Expression.AndAlso(rightLeft, rightRight);//(6)完成整个表达式 Expression<Func<int, int, bool>> lambdaExr = Expression.Lambda<Func<int, int, bool>>(Right,parameters);Console.Write(lambdaExr.ToString());
运行结果如下:
上面创建过程如下:
2.执行表达式树
动态创建完成上面的表达式,我们肯定最终结果是要使用这个表达式进行处理一些问题,对于动创建的表达式树如何执行呢?
这个问题非常容易,只需要两个步聚:
A.Compiling 编程表达式树为委托
B.调用表达式树(调用该委托)
下面看示例:
//执行表达式树 Expression<Func<int, int, bool>> expression = (x, y) => x != 0 && x == y + 1; Func<int, int, bool> result = expression.Compile(); bool result2= expression.Compile()(9,8); Console.WriteLine(result2); Console.WriteLine(result(3, 2)); Console.WriteLine(result(5, 4)); Console.WriteLine(result(6, 4)); Console.WriteLine(result(-6, -7));
运行结果如下:
3.调试表达式树
在调试应用程序时,您可以分析表达式树的结构和内容。 若要快速了解表达式树结构,您可以使用 DebugView 属性,该属性仅在调试模式下可用。 使用 Visual Studio 进行调试。为了更好地表示表达式A.树的内容,DebugView 属性使用 Visual Studio 可视化工具。
在“数据提示”、“监视”窗口、“自动”窗口或“局部变量”窗口中,单击表达式树的 DebugView 属性旁边显示的放大镜图标。将会显示可视化工具列表。
B.单击要使用的可视化工具。
比如我们使用文本可视化工具
$符号,表示 参数
4.修改表达式树
表达式树是不可变的,这意味着不能直接修改表达式树。
若要更改表达式树,必须创建现有表达式树的一个副本,并在创建副本的过程中执行所需更改。 您可以使用 ExpressionVisitor 类遍历现有表达式树,并复制它访问的每个节点。
.NET 有一ExpressionVisitor 类提供重写来修改表达式树
下面我们看一下如何通过重写VisitBinary方法将表达式树左边节点类型由 && 转为 ||,实现如下:
public class OrElseModifier : ExpressionVisitor {public Expression Modify(Expression expression){return Visit(expression);}protected override Expression VisitBinary(BinaryExpression b){if (b.NodeType == ExpressionType.AndAlso){Expression left = this.Visit(b.Left);Expression right = this.Visit(b.Right);return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method);}return base.VisitBinary(b);} }
调用如下:
//修改表达式树 Expression<Func<int, int, bool>> expression = (x, y) => x != 0 && x == y + 1;OrElseModifier amf = new OrElseModifier(); Expression newExp= amf.Modify(expression); Console.WriteLine("原表达式: "+ expression.ToString()); Console.WriteLine("修改后的表达式:"+newExp.ToString());
运行结果如下:
对于上面的实现,有几点要说明,上面.NET提供给我们的类ExpressionVisitor 有很多可重写的方法供我们完成对表达式的间接修改,返回一个表达式副本,也就是新的表达式。
我们在调用阶段为什么要使用Modify(expression);来调用,这点,.net在设计的时候,使用了一种设计模式,就是访问者模式。
我们可以看到VisitBinary是一个保护的成员,当然我们在重写的时候是不能修改原方法的修饰符的。
这一点小伙伴们在[.net 面向对象编程基础] (13) 面向对象三大特性——多态一节中可以详细了解。
对于设计模式,我如果有时间,会写这方面的东西,博客园相关的文章也是非常多。
5.使用表达式树来生成动态查询
我们做一个有意思的示例,分类查询我在博客园中的文章。
第一步,我们先获取文章列表,通过一个实体列表来存放数据
先建立实体:
/// <summary /// 我的博客文章实体类 /// </summary> public class MyArticle {/// <summary>/// 文章编号/// </summary>public int id { get; set; }/// <summary>/// 文章标题/// </summary>public string title { get; set; }/// <summary>/// 文章摘要/// </summary>public string summary { get; set; }/// <summary>/// 发布时间/// </summary>public DateTime published { get; set; }/// <summary>/// 最后更新时间/// </summary>public DateTime updated { get; set; }/// <summary>/// URL地址/// </summary>public string link { get; set; }/// <summary>/// 推荐数/// </summary>public int diggs { get; set; }/// <summary>/// 浏览量/// </summary>public int views { get; set; }/// <summary>/// 评论数/// </summary>public int comments { get; set; }/// <summary>/// 作者/// </summary>public string author { get; set; } }
接下来获取文章
//动态查询 我在博客园中的文章分类查询//第一步,获取我在博客园中的文章 List<MyArticle> myArticleList = new List<MyArticle>(); var document = XDocument.Load("http://wcf.open.cnblogs.com/blog/u/yubinfeng/posts/1/100");var elements = document.Root.Elements();//在进行这个工作之前,我们先获取我博客中的文章列表 var result = elements.Where(m => m.Name.LocalName == "entry").Select(myArticle => new MyArticle {id = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "id").Value),title = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "title").Value,published = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "published").Value),updated = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "updated").Value),diggs = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "diggs").Value),views = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "views").Value),comments = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "comments").Value),summary = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "summary").Value,link = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "link").Attribute("href").Value,author = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "author").Elements().SingleOrDefault(x => x.Name.LocalName == "name").Value }); myArticleList.AddRange(result);
创建一个查询表达式树的方法
public static IQueryable<T> MySearchList(IQueryable<T> myArticleTable, T myArticle) {//第二步,动态查询我的文章// List<MyArticle> SearchMyArticleList = new List<MyArticle>();//1.我们先定义几个查询的参数(文章标题,浏览数,发布时间) ParameterExpression myart = Expression.Parameter(typeof(T), "article"); //标题 ParameterExpression searchTitle = Expression.Parameter(typeof(string), "searchTitle"); //标题 ParameterExpression searchViews = Expression.Parameter(typeof(int), "searchViews"); //浏览数 ParameterExpression searchPublished = Expression.Parameter(typeof(DateTime), "searchPublished");//创建月份//2.使用表达式树,动态生成查询 (查询某个日期的文章)Expression left = Expression.Property(myart, typeof(T).GetProperty("published")); //访问属性的表达式Expression right = Expression.Property(Expression.Constant(myArticle), typeof(T).GetProperty("published"));//访问属性的表达式Expression e1 = Expression.GreaterThanOrEqual(left, right); //大于等于//2.使用表达式树,动态生成查询 (按点击数查询)Expression left2 = Expression.Property(myart, typeof(T).GetProperty("views")); //访问属性的表达式Expression right2 = Expression.Property(Expression.Constant(myArticle), typeof(T).GetProperty("views"));//访问属性的表达式Expression e2 = Expression.GreaterThanOrEqual(left2, right2);//3.构造动态查询 (按点击数和月份查询)Expression predicateBody = Expression.AndAlso(e1, e2);//4.构造过滤MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable),"Where",new Type[] { typeof(T) },myArticleTable.Expression,Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { myart }));//构造排序MethodCallExpression orderByCallExpression = Expression.Call(typeof(Queryable),"OrderByDescending",new Type[] { typeof(T), typeof(int) },whereCallExpression,Expression.Lambda<Func<T, int>>(left2, new ParameterExpression[] { myart }));//创建查询表达式树IQueryable<T> results = myArticleTable.Provider.CreateQuery<T>(orderByCallExpression);return results; }
调用方法
IQueryable<MyArticle> results = ExpressionTree<MyArticle>.MySearchList(myArticleList.AsQueryable<MyArticle>(), new MyArticle() { views=500, published=Convert.ToDateTime("2015-06")});foreach (MyArticle article in results)Console.WriteLine(article.title + " \n [发布日期:"+article.published+"] [浏览数:"+article.views+"]");
运行结果如下:
我们查询的是 发布日期在6月1日以后,点击量在500以上的文章
6.要点:
本节通过动态创建表达式树、执行表达式树及表达式树的调试的学习,最后通过一个动态查询博客园文章结束,使小伙伴们能熟练认识表达式树在动态查询上带来的便利。
[花絮]:晚上写博客过程中,我家汪一直抓了我N次,让我时刻保持清醒状态,最终完成本篇,下面是家汪的靓照:
==============================================================================================
返回目录
<如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>
<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>
<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>
.NET 技术交流群:467189533
==============================================================================================
[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用相关推荐
- [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)...
[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...
- [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手
[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...
- [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门
[.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门 1. 什么是正则表达式? 1.1 正则表达式概念 正则表达式,又称正则表示法,英文名:Regular Expression(简 ...
- [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git...
[.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git--在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...
- [.net 面向对象程序设计进阶] (9) 序列化(Serialization) (一) 二进制流序列化
[.net 面向对象程序设计进阶] (9) 序列化(Serialization) (一) 二进制流序列化 本节导读: 在.NET编程中,经常面向对象处理完以后要转换成另一种格式传输或存储,这种将对 ...
- C++面向对象程序设计课程笔记(第三周)
第一节 this指针 1.C++程序到C程序的翻译 图1 从C++到C C++中的class在C语言中并不存在,因此可以用struct(结构体)来代替,成员变量也可以直接用域来代替,但是类中的成员函数 ...
- 《面向对象程序设计》课程作业 (三)
GitHub GitHub 本次作业的感想 又是一次匆忙中完成的作业啊--开学后果然是没什么时间了.原先不知道四则运算将会有这么多后续,所以当初想怎么写就怎么写,怎么暴力怎么来,没想到给现在造成了这么 ...
- [.net 面向对象程序设计深入](4)MVC 6 —— 谈谈MVC的版本变迁及新版本6.0发展方向...
[.net 面向对象程序设计深入](4)MVC 6 --谈谈MVC的版本变迁及新版本6.0发展方向 1.关于MVC 在本篇中不再详细介绍MVC的基础概念,这些东西百度要比我写的全面多了,MVC从1.0 ...
- [.net 面向对象程序设计深入](36)Redis——基础
[.net 面向对象程序设计深入](36)Redis--基础 很长一段时间没更新博客了,坚持做一件事,真不是件容易的事,后面我会继续尽可能的花时间更新完这个系列文章. 因这个系列的文章涉及的范围太大了 ...
最新文章
- 链表问题4——反转单向链表
- tc溜溜865手机投屏卡_这台手机智商为零,却要挑战小米华为!
- 贷款利率最高多少合法?
- ZooKeeper学习第一期---Zookeeper简单介绍(转)
- hexo博客修改博文中日期的颜色
- css中变量_CSS中的变量
- 2. with check option能起什么作用?_【科普】专家教路:面膜护肤到底有什么用?
- 从事Web前端开发,必须要知道的这些框架
- Arcgis10.2安装及LicensenManager10.2启动失败解决方法
- 常见排序算法以及对应的时间复杂度和空间复杂度
- 精讲RestTemplate第6篇-文件上传下载与大文件流式下载
- windows环境下,CMD控制台查看进程、结束进程相关命令
- 基于opencv答题卡识别
- 盘点那些值得吐槽的计算机术语翻译
- 优化js脚本设计,防止浏览器假死
- Jmeter读取CSV文件
- 秒数转换为天,小时,分钟,秒的公式
- 【Redis】事务不保证原子性、没有隔离级别
- CF 375C Circling Round Treasures [DP(spfa) 状压 射线法]
- 灵遁者:写作没有固定的模型,也不会有固定的路