C#如何在运行时按不同的属性过滤列表

时间:2016-04-19 19:43:30

标签: c# linq list collections

我是C#/ Unity的新手,请原谅我。

我正在编写一个过滤系统,它将在运行时通过类的任何属性过滤列表。 我正在计划建立一些过滤列表的地方(我知道我可以点击服务器获取我需要的列表,但目前只想过滤我已经拥有的数据)

假设我有一个包含4个属性的“MyClass”类列表:“param1”..“param4”

如果我想通过param1和param2正常过滤它,我可以这样做:

List<MyClass> myList = new List<MyClass>(existinglist);
myList = myList.Where(g => g.param1 == somevalue && g.param2 == someothervalue).ToList();

我怎么能在运行时生成相同的where子句?

谢谢!

3 个答案:

答案 0 :(得分:1)

您可以编写如下的扩展方法:

public static IEnumerable<T> Where<T>(this IEnumerable<T> source, string propName, object value)
{
    var type = typeof(T);

    var propInfo = type.GetProperty(propName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

    var parameterExpr = Expression.Parameter( type, "x" ); //x
    var memberAccessExpr = Expression.MakeMemberAccess( parameterExpr, propInfo ); //x.Prop

    var lambda = Expression.Lambda( Expression.Equal(memberAccessExpr, Expression.Constant(value)), 
                                    parameterExpr );         //x=>x.Prop==value

    var mi = typeof(Enumerable)
                .GetMethods()
                .Where(m => m.Name == "Where")
                .First(m => m.GetParameters().Count() == 2)
                .MakeGenericMethod(type);

    return (IEnumerable<T>)mi.Invoke(null, new object[] { source, lambda.Compile() });
}

您现在可以将其用作

var test = new[] { new { a = 1 }, new { a = 2 } }.Where("a", 1).ToList();

答案 1 :(得分:0)

您可以使用辅助方法,该方法根据传递的过滤器列表动态构建和编译lambda。我使用KeyValuePair<string, object>来表示过滤器信息(属性名称为KeyValue - 以及属性值),但当然可以针对其他数据结构进行调整(如自定义类等)

public static class EnumerableExtensions
{
    public static IEnumerable<T> Where<T>(this IEnumerable<T> source, IEnumerable<KeyValuePair<string, object>> filters)
    {
        if (filters == null || !filters.Any()) return source;
        var parameter = Expression.Parameter(typeof(T), "x");
        var body = filters
            .Select(filter => Expression.Equal(
                Expression.PropertyOrField(parameter, filter.Key),
                Expression.Constant(filter.Value)))
            .Aggregate(Expression.AndAlso);
        var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
        return source.Where(predicate.Compile());
    }
}

样本用法:

var filters = new List<KeyValuePair<string, object>>
{
    new KeyValuePair<string, object>("param1", somevalue),
    new KeyValuePair<string, object>("param2", someothervalue),
};
var myList = existinglist.Where(filters).ToList();

答案 2 :(得分:-1)

lambda表达式只是函数的简写。所以你可以用任何带有Myclass并返回bool的函数替换lambda。然后在该方法中编写代码来动态评估您需要的内容 - 如果需要,可以使用反射。

myList = myList.Where(myFunction).ToList();