[.net 面向對象程序設計進階] (7) Lamda表達式(三) 表達式樹高級應用

[.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>

.NET 技術交流羣:467189533    .NET 程序設計

==============================================================================================   

相關文章
相關標籤/搜索