程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 趣味編程:C#中Specification模式的實現(參考答案 - 下)(1)

趣味編程:C#中Specification模式的實現(參考答案 - 下)(1)

編輯:關於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