朋友想實現 source.OrWhere(i => i > 0),但在 LINQ 中 Where 後序列已過濾,無法「補回」被排除元素。這啟發我引入 Specification 模式。
將業務規則封裝為可組合對象:
public interface ISpecification<T> {
bool IsSatisfiedBy(T candidate);
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> Not();
}
public abstract class CompositeSpecification<T> : ISpecification<T> {
public abstract bool IsSatisfiedBy(T candidate);
public ISpecification<T> And(ISpecification<T> o) => new AndSpecification<T>(this, o);
public ISpecification<T> Or(ISpecification<T> o) => new OrSpecification<T>(this, o);
public ISpecification<T> Not() => new NotSpecification<T>(this);
}
public class AndSpecification<T> : CompositeSpecification<T> { ... }
public class OrSpecification<T> : CompositeSpecification<T> { ... }
public class NotSpecification<T> : CompositeSpecification<T> { ... }
public class OddSpecification : CompositeSpecification<int> {
public override bool IsSatisfiedBy(int c) => c % 2 != 0;
}
public class PositiveSpecification : CompositeSpecification<int> {
public override bool IsSatisfiedBy(int c) => c > 0;
}
var odd = new OddSpecification();
var oddOrPositive = odd.Or(new PositiveSpecification());
foreach (var i in Enumerable.Range(-5, 10).Where(x => oddOrPositive.IsSatisfiedBy(x)))
Console.WriteLine(i); // -5 -3 -1 1 2 3 4
標準實現「殺雞用牛刀」,每個規則一個類別太重。C# 擁有委托、擴展方法、Lambda、表達式樹等強大特性,能否實現更輕量、更原生的版本?且朋友真實場景是 LINQ to SQL,需表達式樹組合。請見上/下兩篇參考答案。