【问题标题】:How to apply multiple filter conditions (simultaneously) on a list?如何在列表上(同时)应用多个过滤条件?
【发布时间】:2023-03-14 08:20:02
【问题描述】:

我在.Net 4.0 框架中使用了以下 C# 代码。这是在引用The Specification Pattern - by Jeff Perrin后创建的

GetProducts() 中,要使用的条件在方法内部定义(硬编码)。还有另一种名为GetProductsBasedOnInputFilters() 的方法。在此方法中,规范列表作为该方法的参数。

问题

在这种方法中,在产品列表中应用这些过滤器的最佳方法是什么?

注意:我尝试在foreach 循环中应用FindAll 子句并将结果添加到list 中。但这种逻辑是不正确的 - 只有满足所有条件的项目才需要返回。

注意:productSpecifications 列表中的规格数量会因用户输入而异

注意:“Dynamically build LINQ filter for the Any() method?”中提到的方法似乎很有用。但是我不确定如何在这里使用这种方法,因为我正在处理 specifications 的列表;不是generic delegates

过滤方法

public static class ProductFilterHelper
{
    public static List<Product> GetProducts(List<Product> list)
    {
        double priceLimit = 100;

        //FIRST::
        //List<Product> selectedList =  list.FindAll(new OnSaleSpecification().IsSatisfiedBy);

        //SECOND::
        //AndSpecification<Product> spec = new AndSpecification<Product>(new OnSaleSpecificationForProduct(), new PriceGreaterThanSpecificationForProduct(priceLimit));
        //List<Product> selectedList = list.FindAll(spec.IsSatisfiedBy); 


        //THIRD:
        List<Product> selectedList = list.FindAll(new OnSaleSpecificationForProduct()
                                                       .And(new PriceGreaterThanSpecificationForProduct(priceLimit))
                                                       .And(new PriceGreaterThan105())
                                                       .IsSatisfiedBy
                                                  );

        return selectedList;
    }

    public static List<Product> GetProductsBasedOnInputFilters(List<Product> productList, List<Specification<Product>> productSpeifications)
    {
        List<Product> selectedList = new List<Product>();

        foreach (Specification<Product> specification in productSpeifications)
        {
            List<Product> 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<Product> list = new List<Product>();

        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<Product>> specifications = new List<Specification<Product>>();
        specifications.Add(new OnSaleSpecificationForProduct());
        specifications.Add(new PriceGreaterThanSpecificationForProduct(priceLimit));
        specifications.Add(new PriceGreaterThan105());

        List<Product> selectedList = ProductFilterHelper.GetProductsBasedOnInputFilters(list, specifications);

        Console.ReadKey();
    }

}

抽象规范

public abstract class Specification<T>
{
    public abstract bool IsSatisfiedBy(T obj);

    public AndSpecification<T> And(Specification<T> specification)
    {
        return new AndSpecification<T>(this, specification);
    }

    public OrSpecification<T> Or(Specification<T> specification)
    {
        return new OrSpecification<T>(this, specification);
    }

    public NotSpecification<T> Not(Specification<T> specification)
    {
        return new NotSpecification<T>(this, specification);
    }
}

public abstract class CompositeSpecification<T> : Specification<T>
{
    protected readonly Specification<T> _leftSide;
    protected readonly Specification<T> _rightSide;

    public CompositeSpecification(Specification<T> leftSide, Specification<T> rightSide)
    {
        _leftSide = leftSide;
        _rightSide = rightSide;
    }
}

通用规范

public class AndSpecification<T> : CompositeSpecification<T>
{
    public AndSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {

    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) && _rightSide.IsSatisfiedBy(obj);
    }
}

public class OrSpecification<T> : CompositeSpecification<T>
{
    public OrSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {
    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) || _rightSide.IsSatisfiedBy(obj);
    }
}

public class NotSpecification<T> : CompositeSpecification<T>
{
    public NotSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {
    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) && !_rightSide.IsSatisfiedBy(obj);
    }
}

产品规格

public class OnSaleSpecificationForProduct : Specification<Product>
{
    public override bool IsSatisfiedBy(Product product)
    {
        return product.IsOnSale;
    }
}

public class PriceGreaterThanSpecificationForProduct : Specification<Product>
{
    private readonly double _price;
    public PriceGreaterThanSpecificationForProduct(double price)
    {
        _price = price;
    }

    public override bool IsSatisfiedBy(Product product)
    {
        return product.Price > _price;
    }
}

public class PriceGreaterThan105 : Specification<Product>
{

    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; }
    }
}

参考文献

  1. The Specification Pattern - by Jeff Perrin
  2. Fluent interfaces and Method Chaining in C#
  3. Avoid to browse a list multiple time with linq, with dynamic conditions (filter)
  4. Dynamically build LINQ filter for the Any() method?

【问题讨论】:

标签: c# linq generics design-patterns specification-pattern


【解决方案1】:

您可以做以下几件事之一:

  • 通过将Where 调用堆叠在一起来组合过滤器,就像在@Lijo 的回答中一样

  • 检查每个项目的所有规格:

    return productList
      .Where(p => specifications.All(ps => ps.IsSatisfiedBy(p))
      .ToList()
    
  • 创建一个复合“与”规范,该规范接受多个子节点,而不仅仅是两个:

    public class AndSpecification<T> : ISpecification<T>
    {
        private ISpecification<T>[] _components;
    
        public AndSpecification(ISpecification<T>[] 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);

【讨论】:

  • 谢谢..我打算用All..我目前的代码是return productList.Where(p =&gt; productSpecifications.All(ps =&gt; ps.IsSatisfiedBy(p))).ToList();
【解决方案2】:

以下代码有效...欢迎提出建议。

 public static List<Product> GetProductsBasedOnInputFilters(List<Product> productList, List<Specification<Product>> productSpecifications)
 {
            IEnumerable<Product> selectedList = productList;
            foreach (Specification<Product> specification in productSpecifications)
            {
                selectedList = selectedList.Where(specification.IsSatisfiedBy);
            }
            return selectedList.ToList();
 }

下面的也值得一看..

  1. Expression Tree Basics
  2. Generating Dynamic Methods with Expression Trees in Visual Studio 2010
  3. Dynamically Composing Expression Predicates
  4. How to combine conditions dynamically?

【讨论】:

    猜你喜欢
    • 2021-04-03
    • 1970-01-01
    • 2022-01-24
    • 2020-08-16
    • 2012-10-18
    • 2013-07-21
    • 1970-01-01
    • 2021-12-04
    • 2019-07-30
    相关资源
    最近更新 更多