[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用
[.net?面向?qū)ο蟪绦蛟O(shè)計進(jìn)階]?(7)?Lamda表達(dá)式(三)?表達(dá)式樹高級應(yīng)用
本節(jié)導(dǎo)讀:討論了表達(dá)式樹的定義和解析之后,我們知道了表達(dá)式樹就是并非可執(zhí)行代碼,而是將表達(dá)式對象化后的數(shù)據(jù)結(jié)構(gòu)。是時候來引用他解決問題。而本節(jié)主要目的就是使用表達(dá)式樹解決實際問題。?
讀前必備:?
本節(jié)學(xué)習(xí)前,需要掌握以下知識:?
A.繼承 ? ??[.net?面向?qū)ο缶幊袒A(chǔ)]??(12)?面向?qū)ο笕筇匦?span style="font-family:Verdana;">——繼承?
B.多態(tài) ? ??[.net?面向?qū)ο缶幊袒A(chǔ)]??(13)?面向?qū)ο笕筇匦?span style="font-family:Verdana;">——多態(tài)?
C.抽象類?[.net?面向?qū)ο缶幊袒A(chǔ)]??(15)?抽象類?
D.泛型? ??[.net ?面向?qū)ο缶幊袒A(chǔ)] ? ?(18)?泛型
1.動態(tài)創(chuàng)建表達(dá)式樹?
上一節(jié)中通過對表達(dá)式樹結(jié)構(gòu)和解析表達(dá)式的學(xué)習(xí)以后,動態(tài)創(chuàng)建表達(dá)式樹,已經(jīng)變得非常簡單了,下面我們使用表達(dá)式樹動態(tài)創(chuàng)建下節(jié)的Lambda表達(dá)式.?
先看我們要最終完成的原表達(dá)式:?
Expression<Func<int, int, bool>> expression = (x, y) => x!=0 && x==y+1;?動態(tài)創(chuàng)建過程如下:
//動態(tài)創(chuàng)建表達(dá)式樹 Expression<Func<int, int, bool>> expression = (x, y) => x != 0 && x == y + 1;//先創(chuàng)建兩個參數(shù) ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(typeof(int),"x"), Expression.Parameter(typeof(int), "y") };ParameterExpression param1 = parameters[0]; ParameterExpression param2 = parameters[1];//下面先創(chuàng)建右邊第一個表達(dá)式 x!=0 //(1)常量 0x ConstantExpression rightLeftRight = Expression.Constant(0, typeof(int)); //(2)創(chuàng)建左邊第一個表達(dá)式 x!=0 BinaryExpression rightLeft = Expression.NotEqual(param1, rightLeftRight);//下面創(chuàng)建右邊第二個表達(dá)式 x==y+1 //(3) 先創(chuàng)建右邊部分表達(dá)式y(tǒng)+1 BinaryExpression rightRightRight = Expression.Add(param2, Expression.Constant(1, typeof(int))); //(4)創(chuàng)建右邊表達(dá)式 x==y+1 BinaryExpression rightRight = Expression.Equal(param1, rightRightRight);//5)創(chuàng)建表達(dá)式 右部整體 x != 0 && x == y + 1 BinaryExpression Right = Expression.AndAlso(rightLeft, rightRight);//(6)完成整個表達(dá)式 Expression<Func<int, int, bool>> lambdaExr = Expression.Lambda<Func<int, int, bool>>(Right,parameters);Console.Write(lambdaExr.ToString());運(yùn)行結(jié)果如下:
上面創(chuàng)建過程如下:
2.執(zhí)行表達(dá)式樹
動態(tài)創(chuàng)建完成上面的表達(dá)式,我們肯定最終結(jié)果是要使用這個表達(dá)式進(jìn)行處理一些問題,對于動創(chuàng)建的表達(dá)式樹如何執(zhí)行呢?
這個問題非常容易,只需要兩個步聚:
A.Compiling?編程表達(dá)式樹為委托
B.調(diào)用表達(dá)式樹(調(diào)用該委托)
下面看示例:
//執(zhí)行表達(dá)式樹 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));運(yùn)行結(jié)果如下:
3.調(diào)試表達(dá)式樹
在調(diào)試應(yīng)用程序時,您可以分析表達(dá)式樹的結(jié)構(gòu)和內(nèi)容。?若要快速了解表達(dá)式樹結(jié)構(gòu),您可以使用?DebugView?屬性,該屬性僅在調(diào)試模式下可用。?使用?Visual?Studio?進(jìn)行調(diào)試。為了更好地表示表達(dá)式A.樹的內(nèi)容,DebugView?屬性使用?Visual?Studio?可視化工具。
在“數(shù)據(jù)提示”、“監(jiān)視”窗口、“自動”窗口或“局部變量”窗口中,單擊表達(dá)式樹的?DebugView?屬性旁邊顯示的放大鏡圖標(biāo)。將會顯示可視化工具列表。
B.單擊要使用的可視化工具。
?
比如我們使用文本可視化工具
$符號,表示?參數(shù)
4.修改表達(dá)式樹
表達(dá)式樹是不可變的,這意味著不能直接修改表達(dá)式樹。
若要更改表達(dá)式樹,必須創(chuàng)建現(xiàn)有表達(dá)式樹的一個副本,并在創(chuàng)建副本的過程中執(zhí)行所需更改。?您可以使用?ExpressionVisitor?類遍歷現(xiàn)有表達(dá)式樹,并復(fù)制它訪問的每個節(jié)點(diǎn)。?
.NET?有一ExpressionVisitor?類提供重寫來修改表達(dá)式樹
下面我們看一下如何通過重寫VisitBinary方法將表達(dá)式樹左邊節(jié)點(diǎn)類型由?&&?轉(zhuǎn)為?||,實現(xiàn)如下:
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);} }調(diào)用如下:
//修改表達(dá)式樹 Expression<Func<int, int, bool>> expression = (x, y) => x != 0 && x == y + 1;OrElseModifier amf = new OrElseModifier(); Expression newExp= amf.Modify(expression); Console.WriteLine("原表達(dá)式: "+ expression.ToString()); Console.WriteLine("修改后的表達(dá)式:"+newExp.ToString());運(yùn)行結(jié)果如下:
對于上面的實現(xiàn),有幾點(diǎn)要說明,上面.NET提供給我們的類ExpressionVisitor?有很多可重寫的方法供我們完成對表達(dá)式的間接修改,返回一個表達(dá)式副本,也就是新的表達(dá)式。
我們在調(diào)用階段為什么要使用Modify(expression);來調(diào)用,這點(diǎn),.net在設(shè)計的時候,使用了一種設(shè)計模式,就是訪問者模式。
我們可以看到VisitBinary是一個保護(hù)的成員,當(dāng)然我們在重寫的時候是不能修改原方法的修飾符的。
這一點(diǎn)小伙伴們在[.net?面向?qū)ο缶幊袒A(chǔ)]??(13)?面向?qū)ο笕筇匦?span style="font-family:Verdana;">——多態(tài)一節(jié)中可以詳細(xì)了解。
對于設(shè)計模式,我如果有時間,會寫這方面的東西,博客園相關(guān)的文章也是非常多。??
5.使用表達(dá)式樹來生成動態(tài)查詢
我們做一個有意思的示例,分類查詢我在博客園中的文章。
第一步,我們先獲取文章列表,通過一個實體列表來存放數(shù)據(jù)
先建立實體:
/// <summary /// 我的博客文章實體類 /// </summary> public class MyArticle {/// <summary>/// 文章編號/// </summary>public int id { get; set; }/// <summary>/// 文章標(biāo)題/// </summary>public string title { get; set; }/// <summary>/// 文章摘要/// </summary>public string summary { get; set; }/// <summary>/// 發(fā)布時間/// </summary>public DateTime published { get; set; }/// <summary>/// 最后更新時間/// </summary>public DateTime updated { get; set; }/// <summary>/// URL地址/// </summary>public string link { get; set; }/// <summary>/// 推薦數(shù)/// </summary>public int diggs { get; set; }/// <summary>/// 瀏覽量/// </summary>public int views { get; set; }/// <summary>/// 評論數(shù)/// </summary>public int comments { get; set; }/// <summary>/// 作者/// </summary>public string author { get; set; } }接下來獲取文章
//動態(tài)查詢 我在博客園中的文章分類查詢//第一步,獲取我在博客園中的文章 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();//在進(jìn)行這個工作之前,我們先獲取我博客中的文章列表 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);創(chuàng)建一個查詢表達(dá)式樹的方法
public static IQueryable<T> MySearchList(IQueryable<T> myArticleTable, T myArticle) {//第二步,動態(tài)查詢我的文章// List<MyArticle> SearchMyArticleList = new List<MyArticle>();//1.我們先定義幾個查詢的參數(shù)(文章標(biāo)題,瀏覽數(shù),發(fā)布時間) ParameterExpression myart = Expression.Parameter(typeof(T), "article"); //標(biāo)題 ParameterExpression searchTitle = Expression.Parameter(typeof(string), "searchTitle"); //標(biāo)題 ParameterExpression searchViews = Expression.Parameter(typeof(int), "searchViews"); //瀏覽數(shù) ParameterExpression searchPublished = Expression.Parameter(typeof(DateTime), "searchPublished");//創(chuàng)建月份//2.使用表達(dá)式樹,動態(tài)生成查詢 (查詢某個日期的文章)Expression left = Expression.Property(myart, typeof(T).GetProperty("published")); //訪問屬性的表達(dá)式Expression right = Expression.Property(Expression.Constant(myArticle), typeof(T).GetProperty("published"));//訪問屬性的表達(dá)式Expression e1 = Expression.GreaterThanOrEqual(left, right); //大于等于//2.使用表達(dá)式樹,動態(tài)生成查詢 (按點(diǎn)擊數(shù)查詢)Expression left2 = Expression.Property(myart, typeof(T).GetProperty("views")); //訪問屬性的表達(dá)式Expression right2 = Expression.Property(Expression.Constant(myArticle), typeof(T).GetProperty("views"));//訪問屬性的表達(dá)式Expression e2 = Expression.GreaterThanOrEqual(left2, right2);//3.構(gòu)造動態(tài)查詢 (按點(diǎn)擊數(shù)和月份查詢)Expression predicateBody = Expression.AndAlso(e1, e2);//4.構(gòu)造過濾MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable),"Where",new Type[] { typeof(T) },myArticleTable.Expression,Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { myart }));//構(gòu)造排序MethodCallExpression orderByCallExpression = Expression.Call(typeof(Queryable),"OrderByDescending",new Type[] { typeof(T), typeof(int) },whereCallExpression,Expression.Lambda<Func<T, int>>(left2, new ParameterExpression[] { myart }));//創(chuàng)建查詢表達(dá)式樹IQueryable<T> results = myArticleTable.Provider.CreateQuery<T>(orderByCallExpression);return results; }調(diào)用方法
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 [發(fā)布日期:"+article.published+"] [瀏覽數(shù):"+article.views+"]");運(yùn)行結(jié)果如下:
我們查詢的是 發(fā)布日期在6月1日以后,點(diǎn)擊量在500以上的文章
6.要點(diǎn):
本節(jié)通過動態(tài)創(chuàng)建表達(dá)式樹、執(zhí)行表達(dá)式樹及表達(dá)式樹的調(diào)試的學(xué)習(xí),最后通過一個動態(tài)查詢博客園文章結(jié)束,使小伙伴們能熟練認(rèn)識表達(dá)式樹在動態(tài)查詢上帶來的便利。
[花絮]:晚上寫博客過程中,我家汪一直抓了我N次,讓我時刻保持清醒狀態(tài),最終完成本篇,下面是家汪的靚照:
?
==============================================================================================??
?返回目錄
?<如果對你有幫助,記得點(diǎn)一下推薦哦,如有有不明白或錯誤之處,請多交流>??
<對本系列文章閱讀有困難的朋友,請先看《.net?面向?qū)ο缶幊袒A(chǔ)》>
<轉(zhuǎn)載聲明:技術(shù)需要共享精神,歡迎轉(zhuǎn)載本博客中的文章,但請注明版權(quán)及URL>
.NET 技術(shù)交流群:467189533? ?
============================================================================================== ??
總結(jié)
以上是生活随笔為你收集整理的[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Debian 里设置 IP 地址、网关、
- 下一篇: sas Data步数据读取流程详解