日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

趣味编程:C#中Specification模式的实现(参考答案 - 下)

發(fā)布時間:2024/8/22 C# 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 趣味编程:C#中Specification模式的实现(参考答案 - 下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇文章中我們利用C#語言的特性實現(xiàn)了一種輕量級的Specification模式,它的關鍵在于拋棄了具體的Specification類型,而是使用一個委托對象代替唯一關鍵的IsSatisfiedBy方法邏輯。據(jù)我們分析,其優(yōu)勢之一在于使用簡單,其劣勢之一在于無法靜態(tài)表示。但是它們還都是在處理“業(yè)務邏輯”,如果涉及到一個用于LINQ查詢或其他地方的表達式樹,則問題就不那么簡單了——但也沒有我們想象的那么復雜。

好,那么我們就把場景假想至LINQ上。LINQ與普通業(yè)務邏輯不同的地方在于,它不是用一個IsSatisfiedBy方法或一個委托對象用來表示判斷邏輯,而是需要構造一個表達式樹,一種數(shù)據(jù)結構。如果您還不清楚表達式樹是什么,那么可以看一下腦袋的寫的上手指南。這是.NET 3.5帶來的重要概念,在4.0中又得到了重要發(fā)展,如果您要在.NET方面前進,這是一條必經(jīng)之路。

And、Or和Not之間,最容易處理的便是Not方法,于是我們從這個地方下手,直接來看它的實現(xiàn):

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>對象中主要有兩部分內(nèi)容,一是參數(shù),二是表達式體(Body)。對于Not方法來說,我們只要獲取它的參數(shù)表達式,再將它的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); }

打印出來的自然是所有的奇數(shù),即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); }

這么做雖然能夠編譯通過,但是在執(zhí)行時便會出錯。原因在于one和another兩個表達式雖然都是同樣的形式(Expression<Func<T, bool>>),但是它們的“參數(shù)”不是同一個對象。也就是說,one.Body和another.Body并沒有公用一個ParameterExpression實例,于是我們無論采用哪個表達式的參數(shù),在Expression.Lambda方法調用的時候,都會告訴您新的body中的某個參數(shù)對象并沒有出現(xiàn)在參數(shù)列表中。

于是,我們?nèi)绻獙崿F(xiàn)And和Or,做的第一件事情便是統(tǒng)一兩個表達式樹的參數(shù),于是我們準備一個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;} }

ExpressionVisitor幾乎是處理表達式樹這種數(shù)據(jù)結構的不二法門,它可以用于求值,變形(其實是生成新的結構,因為表達式樹是immutable的數(shù)據(jù)結構)等各種操作。例如,解決表達式樹的緩存時用它來求樹的散列值或讀寫前綴樹,快速計算表達式時用它來提取表達式樹的參數(shù),并將不同的表達式樹“標準化”為相同的結構。

ExpressionVisitor基類的關鍵,就在于提供了遍歷表達式樹的標準方式,如果您直接繼承這個類并調用Visit方法,那么最終返回的結果便是傳入的Expression參數(shù)本身。但是,如果您覆蓋的任意一個方法,返回了與傳入時不同的對象,那么最終的結果就會是一個新的Expression對象。ExpressionVisitor類中的每個方法都負責一類表達式,也都都遵循了類似的原則:它們會遞歸地調用Visit方法,如果Visit返回新對象,那么它們也會構造新對象并返回。

ParameterReplacer類的作用是將一個表達式樹里的所有ParameterExpression替換成我們指定的新對象,因此只需覆蓋VisitParameter方法就可以了。有了它之后,And和Or方法的實現(xiàn)輕而易舉:

public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another) {var candidateExpr = Expression.Parameter(typeof(T), "candidate");var parameterReplacer = new ParameterReplacer(candidateExpr);var left = parameterReplacer.Replace(one.Body);var right = parameterReplacer.Replace(another.Body);var body = Expression.And(left, right);return Expression.Lambda<Func<T, bool>>(body, candidateExpr); }public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another) {var candidateExpr = Expression.Parameter(typeof(T), "candidate");var parameterReplacer = new ParameterReplacer(candidateExpr);var left = parameterReplacer.Replace(one.Body);var right = parameterReplacer.Replace(another.Body);var body = Expression.Or(left, right);return Expression.Lambda<Func<T, bool>>(body, candidateExpr); }

于是,我們最終構造得到的Expression<Func<T, bool>>對象便可以傳入一個LINQ Provider,最終得到查詢結果:

Expression<Func<int, bool>> f = i => i % 2 == 0; f = f.Not().And(i => i % 3 == 0).Or(i => i % 4 == 0);foreach (var i in new int[] { 1, 2, 3, 4, 5, 6 }.AsQueryable().Where(f)) {Console.WriteLine(i); }

輸出的結果是3和4。

這種做法是非常有實用價值的。因為有了LINQ,因此許多朋友都會選擇在數(shù)據(jù)訪問層暴露一個這樣的方法給上層調用:

class ProductDao {public Product GetProduct(Expression<Func<Product, bool>> predicate){...} }

但是您有沒有想過這么做的缺點是什么呢?這么做的缺點便是“過于自由”。由于GetProduct方法只將參數(shù)限制為一個Expression<Func<Product, bool>>對象,因此在調用的時候,我們可以使用任意的形式進行傳遞。因此,外層完全有可能傳入一個目前LINQ Provider不支持的表達式樹形式,也完全有可能傳入一個雖然支持,但會導致查詢速度慢,影響項目整體性能的表達式樹。前者要在運行時才拋出異常,而后者則引發(fā)的性能問題則更難發(fā)現(xiàn)。因此我認為,數(shù)據(jù)訪問層不應該如此自由,它要做出限制。而限制的方式,便是使用Query Object模式,讓GetProduct方法接受一個受限的Criteria對象:

public abstract class ProductCriteria {internal ProductCriteria(Expression<Func<Product, bool>> query){this.Query = query;}public Expression<Func<Product, bool>> Query { get; private set; } }class ProductDao {public Product GetProduct(ProductCriteria predicate){...} }

而在使用時,我們只提供有限的幾種條件,如:

public class ProductIdEqCriteria : ProductCriteria {public ProductIdEqCriteria(int id): base(p => p.ProductID == id){ } }public class ProductViewRangeCriteria : ProductCriteria {public ProductViewRangeCriteria(int min, int max): base(p => p.ViewCount > min && p.ViewCount < max){ } }

再加上配套的擴展方法用于And,Or,Not,于是一切盡在掌握。現(xiàn)在再去瞅瞅原Query Object模式中復雜的實現(xiàn),您是否會有一種滿足感?

轉載于:https://www.cnblogs.com/JeffreyZhao/archive/2009/09/29/specification-pattern-in-csharp-practice-answer-2.html

總結

以上是生活随笔為你收集整理的趣味编程:C#中Specification模式的实现(参考答案 - 下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。