首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

趣味程式設計:C# 中 Specification 模式的實現(表達式樹篇 - 上)

     編輯:關於C語言

上一篇文章中我們利用 C# 語言特性實現了一種輕量級的 Specification 模式,其關鍵在於拋棄具體的 Specification 類型,改用委託物件代替唯一關鍵的 IsSatisfiedBy 方法邏輯。據分析,其優勢在於使用簡單,劣勢在於無法靜態表示。但它們都在處理「業務邏輯」,若涉及用於 LINQ 查詢或其他地方的表達式樹,問題就不那麼簡單了——但也沒有想像中那麼複雜。

好,我們把場景假想至 LINQ。LINQ 與普通業務邏輯不同之處在於:它不是用 IsSatisfiedBy 方法或委託物件表示判斷邏輯,而是需要構造一個表達式樹——一種資料結構。若您不清楚表達式樹是什麼,可參考相關入門指南。這是 .NET 3.5 帶來的重要概念,在 4.0 中又得重要發展,若要在 .NET 方面深入,這是必經之路。

And、Or 和 Not 之中,最容易處理的是 Not 方法,我們從這裡下手,直接看實現:

public static class SpecExprExtensions
{
    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> one)
    {
        var candidateExpr = one.Parameters[0];
        var body = Expression.Not(one.Body);

        return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
    }
}

一個 Expression<TDelegate> 物件主要有兩部分:參數與表達式體(Body)。對於 Not 方法,只需獲取其參數表達式,再將 Body 外包一個 Not 表達式,即可構造新表達式。這部分邏輯非常簡單,了解表達式樹基本結構即可理解。試驗一下:

Expression<Func<int, bool>> f = i => i % 2 == 0;
f = f.Not();

foreach (var i in new int[] { 1, 2, 3, 4, 5, 6 }.AsQueryable().Where(f))
{
    Console.WriteLine(i);
}

輸出自然是所有奇數:1、3、5。

而 And 和 Or 處理上會有麻煩,不能像 Not 一樣簡單處理:

public static Expression<Func<T, bool>> And<T>(
    this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
    var candidateExpr = one.Parameters[0];
    var body = Expression.And(one.Body, another.Body);
    return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}

這樣雖能編譯通過,但執行時會出錯。原因在於 one 和 another 兩個表達式雖形式相同(都是 Expression<Func<T, bool>>),但它們的「參數」不是同一個物件。也就是說,one.Body 和 another.Body 並未共用一個 ParameterExpression 實例,無論採用哪個表達式的參數,在 Expression.Lambda 呼叫時都會告訴您新 Body 中的某個參數物件未出現在參數列表中。

因此,實現 And 和 Or 的第一件事便是統一兩個表達式樹的參數,於是我們準備一個 ExpressionVisitor:

internal class ParameterReplacer : ExpressionVisitor
{
    public ParameterReplacer(ParameterExpression paramExpr)
    {
        this.ParameterExpression = paramExpr;
    }

    public ParameterExpression ParameterExpression { get; private set; }

    public Expression Replace(Expression expr)
    {
        return this.Visit(expr);
    }

    protected override Expression VisitParameter(ParameterExpression p)
    {
        return this.ParameterExpression;
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved