技术开发 频道

C#中Specification模式的实现

  【IT168 技术文档】我们利用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中的某个参数对象并没有出现在参数列表中。

0
相关文章