筛选列表对象的通用方法

w2o*_*ves 5 c# generics filter

我正在尝试创建一个带有三个参数的泛型方法.1)列表集合2)字符串PropertyName 3)字符串FilterString

我们的想法是传递一个对象集合,对象属性的名称和过滤条件,它返回一个对象列表,其中属性包含FilterString.

此外,PropertyName是可选的,因此如果未提供,我想在任何属性中返回包含FilterString的所有对象.

任何关于此的指针都会非常有用.

我想尝试这样的方法签名:public static List FilterList(List collection,String FilterString,String Property ="")

这样我可以从任何地方调用此方法并将其传递给任何List,它将返回一个筛选列表.

rae*_*ae1 7

您可以使用LINQ执行您想要的操作,因此,

var collection = ...
var filteredCollection = 
    collection.Where(item => item.Property == "something").ToList();
Run Code Online (Sandbox Code Playgroud)

否则,你可以尝试反射,

public List<T> Filter<T>(
    List<T> collection, 
    string property, 
    string filterValue)
{
    var filteredCollection = new List<T>();
    foreach (var item in collection)
    {
         // To check multiple properties use,
         // item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)

         var propertyInfo = 
             item.GetType()
                 .GetProperty(property, BindingFlags.Public | BindingFlags.Instance);
         if (propertyInfo == null)
             throw new NotSupportedException("property given does not exists");             

         var propertyValue = propertyInfo.GetValue(item, null);
         if (propertyValue == filterValue)
             filteredCollection.Add(item);       
    }

    return filteredCollection;
}
Run Code Online (Sandbox Code Playgroud)

此解决方案的问题是,对属性名称或拼写错误的更改会导致运行时错误,而不是使用名称为硬类型的实际属性表达式的编译错误.

另外,请注意,根据绑定的标志,这将只上班public,non-static性质.您可以通过传递不同的标志来修改此类行为.


Mar*_*kus 5

为此,您可以结合使用反射和动态表达式。我整理了一个乍一看可能有点长的样本。但是,它符合您的要求并通过以下方式解决这些问题

  • 使用反射查找字符串类型的属性并匹配属性名称 - 如果提供。
  • 创建一个表达式,该表达式对已识别的所有属性调用 string.Contains。如果已识别多个属性,则对 string.Contains 的调用由 Or 表达式组合。这个过滤器表达式被编译并作为参数传递给 Where 扩展方法。使用表达式过滤提供的列表。

按照此链接运行示例。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;

public class Test
{
    public static IEnumerable<T> SelectItems<T>(IEnumerable<T> items, string propName, string value)
    {
        IEnumerable<PropertyInfo> props;
        if (!string.IsNullOrEmpty(propName))
            props = new PropertyInfo[] { typeof(T).GetProperty(propName) };
        else
            props = typeof(T).GetProperties();
        props = props.Where(x => x != null && x.PropertyType == typeof(string));
        Expression lastExpr = null;
        ParameterExpression paramExpr = Expression.Parameter(typeof(T), "x");
        ConstantExpression valueExpr = Expression.Constant(value);
        foreach(var prop in props)
        {
            var propExpr = GetPropertyExpression(prop, paramExpr, valueExpr);
            if (lastExpr == null)
                lastExpr = propExpr;
            else
                lastExpr = Expression.MakeBinary(ExpressionType.Or, lastExpr, propExpr);
        }
        if (lastExpr == null)
            return new T[] {};
        var filterExpr = Expression.Lambda(lastExpr, paramExpr);
        return items.Where<T>((Func<T, bool>) filterExpr.Compile());
    }

    private static Expression GetPropertyExpression(PropertyInfo prop, ParameterExpression paramExpr, ConstantExpression valueExpr)
    {
        var memberAcc = Expression.MakeMemberAccess(paramExpr, prop);
        var containsMember = typeof(string).GetMethod("Contains");
        return Expression.Call(memberAcc, containsMember, valueExpr);
    }

    class TestClass
    {
        public string SomeProp { get; set; }
        public string SomeOtherProp { get; set; }
    }

    public static void Main()
    {
        var data = new TestClass[] {
            new TestClass() { SomeProp = "AAA", SomeOtherProp = "BBB" }, 
            new TestClass() { SomeProp = "BBB", SomeOtherProp = "CCC" }, 
            new TestClass() { SomeProp = "CCC", SomeOtherProp = "AAA" }, 
        };
        var result = SelectItems(data, "", "A");
        foreach(var item in result)
            Console.WriteLine(item.SomeProp);
    }
}
Run Code Online (Sandbox Code Playgroud)

与完全基于反射的方法相比,这种方法只组装过滤器表达式一次并编译它,因此我期望(小)性能改进。