[.net 面向對象程序設計進階] (7) Lamda表達式(三) 表達式樹高級應用html
本節導讀:討論了表達式樹的定義和解析以後,咱們知道了表達式樹就是並不是可執行代碼,而是將表達式對象化後的數據結構。是時候來引用他解決問題。而本節主要目的就是使用表達式樹解決實際問題。 express
讀前必備: 編程
本節學習前,須要掌握如下知識: 設計模式
A.繼承 [.net 面向對象編程基礎] (12) 面向對象三大特性——繼承 數據結構
B.多態 [.net 面向對象編程基礎] (13) 面向對象三大特性——多態 ide
C.抽象類 [.net 面向對象編程基礎] (15) 抽象類 工具
D.泛型 [.net 面向對象編程基礎] (18) 泛型post
1.動態建立表達式樹 學習
上一節中經過對錶達式樹結構和解析表達式的學習之後,動態建立表達式樹,已經變得很是簡單了,下面咱們使用表達式樹動態建立下節的Lambda表達式. this
先看咱們要最終完成的原表達式:
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>
==============================================================================================