[.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!= && x==y+;

动态创建过程如下:

//动态创建表达式树
Expression<Func<int, int, bool>> expression = (x, y) => x != && x == y + ; //先创建两个参数
ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(typeof(int),"x"), Expression.Parameter(typeof(int), "y") }; ParameterExpression param1 = parameters[];
ParameterExpression param2 = parameters[]; //下面先创建右边第一个表达式 x!=0
//(1)常量 0x
ConstantExpression rightLeftRight = Expression.Constant(, typeof(int));
//(2)创建左边第一个表达式 x!=0
BinaryExpression rightLeft = Expression.NotEqual(param1, rightLeftRight); //下面创建右边第二个表达式 x==y+1
//(3) 先创建右边部分表达式y+1
BinaryExpression rightRightRight = Expression.Add(param2, Expression.Constant(, 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 != && x == y + ;
Func<int, int, bool> result = expression.Compile();
bool result2= expression.Compile()(,);
Console.WriteLine(result2);
Console.WriteLine(result(, ));
Console.WriteLine(result(, ));
Console.WriteLine(result(, ));
Console.WriteLine(result(-, -));

运行结果如下:

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 != && x == y + ; 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=, 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表达式(三) 表达式树高级应用的更多相关文章

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

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

  2. [.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托

    [.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托 本节导读: 通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰. ...

  3. [.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类

    [.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类 本节导读:本节主要介绍通过序列 ...

  4. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

    [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...

  5. [.net 面向对象程序设计进阶] (1) 开篇

    [.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...

  6. [.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)

    [.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上) 本节导读: 随着硬件和网络的高速发展,为多线程(Multithreading) ...

  7. [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

    [.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...

  8. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

  9. [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门

    [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门 1. 什么是正则表达式? 1.1 正则表达式概念 正则表达式,又称正则表示法,英文名:Regular Expression(简 ...

随机推荐

  1. 一个Angular模块中可以声明哪些组件?

    一个Angular模块中可以声明哪些组件? (1) controller        控制器 (2) directive                指令 (3) function         ...

  2. SharePoint 2013开发入门探索(一)- 自定义列表

    在SharePoint 2013中创建自定义列表的方式有很多,在网站内容页面添加应用程序就可以创建(站点内容-〉 您的应用程序),也可以通过SharePoint Designer 2013创建,而本文 ...

  3. 自定义滚动条——控制div的大小和透明度

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 31.0px Consolas; color: #2b7ec3 } p.p2 { margin: 0.0px ...

  4. IE事件模型,如何给IE和非IE浏览器添加事件

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title> ...

  5. 让C++程序打印自身源码

    本人原创文章,欢迎阅读,禁止转载. 这绝对是惊艳到让你眼前一亮(为了简洁,故意没考虑资源问题和编译警告). #include <iostream> #include <fstream ...

  6. 基于 Winform + DotNetBar 写的股市行情助手

    StockViewer 股市行情助手 简介 观看股市行情,窗口太显眼,是否担心被身后的老板发现? 窗口来回切换,工作时每隔几分钟就要看一眼股市.难道只能同时做一件事情吗? 现在,一款完全免费.开源的小 ...

  7. JAVA里面的IO流(一)分类1(字节/字符和输入/输出)

      java.io包中定义了多个流类型(流或抽象类)来实现输入/输出功能:可以从不同的角度对其进行分类: 按数据流的方向不同可以分为输入流和输出流 从文件读数据为输入流:往文件写数据为输出流 按处理数 ...

  8. CentOS 6 中安装Node.js 4.0 版本或以上

    如果想在CentOS 6 中安装Node.js >4.0,如果通过以往的方式安装: wget http://nodejs.org/dist/v4.0.0/node-v4.0.0.tar.gz t ...

  9. c++ STL中的vector与list为什么没有提供find操作?

    map里有,set里也有,vector,list没有,太不公平了吧. 其实应该考虑为什么map,set里有find操作. include<algorithm>里有通用的find操作,通用的 ...

  10. 04、AngularJS的ng-bind、多个控制器和apply

    这篇,讲一下angularjs的ng-bind指令,多个控制器,以及手动触发angularjs的脏检查,我直接把代码贴,顺着代码讲. <!DOCTYPE html> <html> ...