如何在列表中应用多个过滤条件(同时)?
我使用.Net 4.0
框架跟随C#代码。 这是在参考规范模式后由Jeff Perrin创建的
在GetProducts()
中,要使用的条件在方法内定义(硬编码)。 还有另一种名为GetProductsBasedOnInputFilters()
方法。 在此方法中,规范列表作为方法的参数。
题
在此方法中,在产品列表中应用这些filter的最佳方法是什么?
注意 :我尝试在foreach
循环中应用FindAll
子句并将结果添加到list
。 但是这种逻辑是不正确的 – 只需要返回那些满足所有条件的项目。
注意 :productSpeifications列表中的规格数量会因用户输入而异
注意 :“ 为Any()方法动态构建LINQfilter? ”中提到的方法似乎很有用。 但是我不知道如何在这里使用这种方法,因为我正在处理specifications
列表; 不是generic delegates
。
过滤方法
public static class ProductFilterHelper { public static List GetProducts(List list) { double priceLimit = 100; //FIRST:: //List selectedList = list.FindAll(new OnSaleSpecification().IsSatisfiedBy); //SECOND:: //AndSpecification spec = new AndSpecification(new OnSaleSpecificationForProduct(), new PriceGreaterThanSpecificationForProduct(priceLimit)); //List selectedList = list.FindAll(spec.IsSatisfiedBy); //THIRD: List selectedList = list.FindAll(new OnSaleSpecificationForProduct() .And(new PriceGreaterThanSpecificationForProduct(priceLimit)) .And(new PriceGreaterThan105()) .IsSatisfiedBy ); return selectedList; } public static List GetProductsBasedOnInputFilters(List productList, List<Specification> productSpeifications) { List selectedList = new List(); foreach (Specification specification in productSpeifications) { List currentList = productList.FindAll(specification.IsSatisfiedBy); if (currentList != null && currentList.Count > 0) { foreach (Product p in currentList) { if (!selectedList.Contains(p)) { selectedList.Add(p); } } } } return selectedList; } }
客户
class Program { static void Main(string[] args) { List list = new List(); Product p1 = new Product(false, 99); Product p2 = new Product(true, 99); Product p3 = new Product(true, 101); Product p4 = new Product(true, 110); Product p5 = new Product(false, 110); list.Add(p1); list.Add(p2); list.Add(p3); list.Add(p4); list.Add(p5); double priceLimit = 100; List<Specification> specifications = new List<Specification>(); specifications.Add(new OnSaleSpecificationForProduct()); specifications.Add(new PriceGreaterThanSpecificationForProduct(priceLimit)); specifications.Add(new PriceGreaterThan105()); List selectedList = ProductFilterHelper.GetProductsBasedOnInputFilters(list, specifications); Console.ReadKey(); } }
摘要规格
public abstract class Specification { public abstract bool IsSatisfiedBy(T obj); public AndSpecification And(Specification specification) { return new AndSpecification(this, specification); } public OrSpecification Or(Specification specification) { return new OrSpecification(this, specification); } public NotSpecification Not(Specification specification) { return new NotSpecification(this, specification); } } public abstract class CompositeSpecification : Specification { protected readonly Specification _leftSide; protected readonly Specification _rightSide; public CompositeSpecification(Specification leftSide, Specification rightSide) { _leftSide = leftSide; _rightSide = rightSide; } }
通用规范
public class AndSpecification : CompositeSpecification { public AndSpecification(Specification leftSide, Specification rightSide) : base(leftSide, rightSide) { } public override bool IsSatisfiedBy(T obj) { return _leftSide.IsSatisfiedBy(obj) && _rightSide.IsSatisfiedBy(obj); } } public class OrSpecification : CompositeSpecification { public OrSpecification(Specification leftSide, Specification rightSide) : base(leftSide, rightSide) { } public override bool IsSatisfiedBy(T obj) { return _leftSide.IsSatisfiedBy(obj) || _rightSide.IsSatisfiedBy(obj); } } public class NotSpecification : CompositeSpecification { public NotSpecification(Specification leftSide, Specification rightSide) : base(leftSide, rightSide) { } public override bool IsSatisfiedBy(T obj) { return _leftSide.IsSatisfiedBy(obj) && !_rightSide.IsSatisfiedBy(obj); } }
产品规格
public class OnSaleSpecificationForProduct : Specification { public override bool IsSatisfiedBy(Product product) { return product.IsOnSale; } } public class PriceGreaterThanSpecificationForProduct : Specification { private readonly double _price; public PriceGreaterThanSpecificationForProduct(double price) { _price = price; } public override bool IsSatisfiedBy(Product product) { return product.Price > _price; } } public class PriceGreaterThan105 : Specification { public override bool IsSatisfiedBy(Product product) { return product.Price > 105; } }
实体
public class Product { private bool _isOnSale; private double _price = 0.0; public Product(bool isOnSale) : this(isOnSale, 0.0) { _isOnSale = isOnSale; } public Product(double price) : this(false, price) { _price = price; } public Product(bool isOnSale, double price) { _price = price; _isOnSale = isOnSale; } public bool IsOnSale { get { return _isOnSale; } } public double Price { get { return _price; } } }
参考
- 规范模式 – 由Jeff Perrin撰写
- C#中的流畅接口和方法链接
- 避免使用linq多次浏览列表,具有动态条件(filter)
- 动态构建Any()方法的LINQfilter?
你可以做以下几件事之一:
-
通过堆叠
Where
调用彼此之上的过滤来组合filter,就像@Lijo的答案一样 -
检查每个项目的所有规格:
return productList .Where(p => specifications.All(ps => ps.IsSatisfiedBy(p)) .ToList()
-
创建一个复合’And’规范,接受多个子节点而不是两个:
public class AndSpecification
: ISpecification { private ISpecification [] _components; public AndSpecification(ISpecification [] components) { _components = components; } public bool IsSatisfiedBy(T item) { return components.All(c => c.IsSatisfiedBy(item)); } }
然后你可以这样做:
var allFiltersSpecification = new AndSpecification(specifications) return productList.Where(allFiltersSpecification.IsSatisfiedBy);
以下代码工作…欢迎提出建议。
public static List GetProductsBasedOnInputFilters(List productList, List> productSpecifications) { IEnumerable selectedList = productList; foreach (Specification specification in productSpecifications) { selectedList = selectedList.Where(specification.IsSatisfiedBy); } return selectedList.ToList(); }
值得一看的还有以下内容……
- 表达树基础知识
- 在Visual Studio 2010中使用表达式树生成动态方法
- 动态编写表达式谓词
- 如何动态组合条件?