按集合过滤IEnumerable(Linq)

时间:2014-06-20 22:03:31

标签: c# linq

我想通过它正在收集的任何对象的特定属性来过滤IEnumerable对象。我想要选项按一个或多个属性值进行过滤,但要过滤的值(以及值)仅在运行时已知。

好的,举个例子,收集的对象可以是以下结构:

public struct Person
{
    public string Name { get; set; }
    public string Profession{ get; set; }
}

然后可以通过以下列表使用此结构,我已使用一些任意值填充该表:

List<Person> people= new List<Person>;
people.Add(new Person(){Name = "Mickey", Profession="Tinker"};
people.Add(new Person(){Name = "Donald", Profession="Tailor"};
people.Add(new Person(){Name = "Goofy", Profession="Soldier"};
people.Add(new Person(){Name = "Pluto", Profession="Spy"};

然后将其放入IEnumerable(所有这些都首先转移到它)

var wantedPeople = from n in this.people select n;

所以说用户只对&#34; Tailor&#34;和&#34;间谍&#34;专业,并通过某种gui技巧创建了以下集合:

List<string> wantedProfessions = new List<string>();
wantedProfessions.Add("Tailor");
wantedProfessions.Add("Spy");

现在我可以使用Linq语句来提交我想要的人,所以它只包括裁缝和间谍条目? 我知道我可以使用where子句,但我不知道如何定制它以获得我想要的东西(并且执行以下操作不是我想要的因为它只适用于wantedProfessions集合上面(例如,此集合将在运行时更改):

wantedPeople = from n in wantedPeople
              where n.Profession == wantedProffessions[0] || n.Profession == wantedProffessions[1]
              select n;

2 个答案:

答案 0 :(得分:4)

如果您想从给定列表中检查任何想要的职业:

wantedPeople = from n in wantedPeople
               where wantedProffessions.Contains(n.Profession)
               select n;

或者您可以通过逐个应用过滤器来构建具有lambda语法的查询:

var query = people.AsEnumerable();

if (!String.IsNullOrEmpty(name))
    query = query.Where(p => p.Name == name);

if (wantedProfessions.Any())
    query = query.Where(p => wantedProfessions.Contains(p.Profession));

如果您想创建更复杂的过滤器,例如某些名称和多个专业,您可以使用Specification pattern。规范可以通过这个简单的界面来定义:

public interface ISpecification<T>
{
   bool Satisfied(T entity);
}

它只检查给定实体(人)是否满足规范。规格看起来也很简单:

public class PersonNameSpecification : ISpecification<Person>
{
    private string _name;
    public PersonNameSpecification(string name)
    {
        _name = name;
    }

    public bool Satisfied(Person person)
    {
        return person.Name == _name;
    }
}

专业规范:

public class PersonProfessionSpecification : ISpecification<Person>
{
    private string[] _professions;
    public PersonProfessionSpecification(params string[] professions)
    {
        _professions = professions;
    }
    public bool Satisfied(Person person)
    {
        return _professions.Contains(person.Profession);
    }
}

您可以创建实现布尔逻辑的规范,如OrSpecification或AndSpecification:

public class AndSpecification<T> : ISpecification<T>
{
    private ISpecification<T> _specA;
    private ISpecification<T> _specB;
    public AndSpecification(ISpecification<T> specA, ISpecification<T> specB)
    {
        _specA = specA;
        _specB = specB;
    }
    public bool Satisfied(T entity)
    {
        return _specA.Satisfied(entity) && _specB.Satisfied(entity);
    }
}

public static class SpecificationExtensions
{
    public static ISpecification<T> And<T>(
       this ISpecification<T> specA, ISpecification<T> specB)
    {
        return new AndSpecification<T>(specA, specB);
    }
}

现在,您可以创建描述您想要的人的复杂规范:

var professionSpec = new PersonProfessionSpecification("Tailor", "Spy");
var nameSpec = new PersonNameSpecification("Pluto");
var spec = professionSpec.And(nameSpec);

获得必要的人:

var result = people.Where(spec.Satisfied);

答案 1 :(得分:0)

谢尔盖B的解决方案对你的例子来说是正确的。

假设您没有使用包含Contains()方法的集合,您还可以执行以下操作:

var wantedPeople = from n in people
                   from p in wantedProffessions
                   where n.Profession.Equals(p)
                   select n;