【问题标题】:How can I search in a list for elements that contain a substring in any of their property values?如何在列表中搜索在其任何属性值中包含子字符串的元素?
【发布时间】:2014-01-17 09:38:01
【问题描述】:

我正在尝试在具有很多属性的对象列表中进行搜索。寻找一个优雅的解决方案,例如可以根据搜索字符串返回结果的 lambda 表达式。这是我的简化形式的数据源。我实际的Customer 类有更多属性:

public static class CustomerDataSource
{
    public static List<Customer> customerData
    {
        get
        {
            Customer customer1 = new Customer() { name = "Bert", address = "London" };
            Customer customer2 = new Customer() { name = "Jon", address = "New York" };
            List<Customer> listCustomers = new List<Customer>();
            listCustomers.Add(customer1);
            listCustomers.Add(customer2);
            return listCustomers;
        }
    }
}

public class Customer
{
    public string name { get; set; }
    public string address { get; set; }
}

什么样的语句可以返回所有包含'searchstring'的Customers。例如,如果搜索字符串是“e”,那么它应该返回 customer1customer2,因为 customer1nameaddress 属性都包含“e”。

我可以编写类似以下的内容,但是我必须为每个属性编写一行代码:

var list = CustomerDataSource.customerData.FindAll(x => x.address.Contains("e"));
var list2 = CustomerDataSource.customerData.FindAll(x => x.name.Contains("e"));

【问题讨论】:

    标签: c# list lambda


    【解决方案1】:

    您可以使用反射来获取所有属性并将其与 LINQ 结合:

    var list = CustomerDataSource
               .customerData
               .Where(c => c.GetType()
                            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                            .Any(pi => pi.GetValue(c).ToString().Contains("York")))
               .ToList();
    

    上面的代码只返回customer2

    但如果您现在确实想在查询期间搜索所有属性,我会采用属性化方法 - 使用自定义属性标记您要搜索的属性:

    [AttributeUsage(AttributeTargets.Property)]
    sealed class SearchableAttribute : Attribute
    {
        public SearchableAttribute()
        {
        }
    }
    

    然后相应地修改您的类,例如,标记只应使用name

    public class Customer
    {
        [Searchable]
        public string name { get; set; }
        public string address { get; set; }
    }
    

    并稍微修改上面的 LINQ 查询:

    var list = CustomerDataSource
               .customerData
               .Where(c => c.GetType()
                            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                            .Where(pi => pi.GetCustomAttribute<SearchableAttribute>() != null)
                            .Any(pi => pi.GetValue(c).ToString().Contains("York")))
               .ToList();
    

    注意: 正如 Sam 所说,aboce 代码假定您对具有合适.ToString() 实现的类型的属性感兴趣。因此,例如,在数组的情况下它不起作用。您必须进一步修改此代码以满足您的需要。以下是使用合适的.ToString() 方法对数组进行额外处理的示例:

    var list = CustomerDataSource
               .customerData
               .Where(c => c.GetType()
               .GetProperties(BindingFlags.Instance | BindingFlags.Public)
               .Where(pi => pi.GetCustomAttribute<SearchableAttribute>() != null)
               .Any(pi => pi.PropertyType.IsArray 
                              ? (pi.GetValue(c) as IEnumerable<object>)
                                   .Any(o => o.ToString().Contains("Paris"))
                              : pi.GetValue(c).ToString().Contains("Paris")))
    

    【讨论】:

    • 请注意,上面的代码假定任何类型的公共实例属性都是合适的。它还假设应该搜索每个属性值的ToString 结果。如果您决定采用这种方法,请务必根据您的具体要求对其进行自定义。
    • 如果其中一个属性是字符串数组怎么办?
    猜你喜欢
    • 2020-02-01
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多