列表<t>上的Lambda并使用Reflection获取属性名称</t>

时间:2010-01-25 15:58:47

标签: c# generics lambda

假设您有一个具有List<T> Items;

的通用类

现在想想这个基本的lambda表达式:

var result = Items.FindAll(x => x.Name = "Filip");

只有当我们知道T的属性时,这才会起作用,当它是通用类型时,你不会这样做。

因此我想使用这样的反射来获取属性:

PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public);

并以某种方式将上述Lambda表达式与之结合,以便它搜索Type的所有公共属性并查看它是否包含“Filip”,此时我并不关心property-name是否为Name。

这可能吗?

2 个答案:

答案 0 :(得分:11)

var result = Items.FindAll(x => 
    properties.Any(p => p.PropertyType == typeof(string) && 
                        p.GetValue(x, null) == "Filip"));

显然,这是一个简单,乐观的字符串比较(例如,您可能希望使用string.Compare),但这应该可以明确这个想法。

修改

dtb在使用表达式树时提出了一个很好的建议。你可以用这样更快的方式完成你所追求的目标:

public static class PropertyScanner
{
    static Func<TType, bool> CreatePredicate<TType, TValue>(TValue value, IEqualityComparer<TValue> comparer)
    {
        var arg = Expression.Parameter(typeof(TType), "arg");

        Expression body = null;

        Expression<Func<TValue, TValue, bool>> compare = (val1, val2) => comparer.Equals(val1, val2);

        foreach (PropertyInfo property in typeof(TType).GetProperties(BindingFlags.Public))
        {
            if (property.PropertyType == typeof(TValue) || typeof(TValue).IsAssignableFrom(property.PropertyType))
            {
                Expression prop = Expression.Equal(Expression.Invoke(compare, new Expression[]
                                       {
                                           Expression.Constant(value),
                                           Expression.Property(arg, property.Name)
                                       }),
                                               Expression.Constant(0));

                if (body == null)
                {
                    body = prop;
                }
                else
                {
                    body = Expression.OrElse(body, prop);
                }
            }
        }

        return Expression.Lambda<Func<TType, bool>>(body, arg).Compile();
    }

    public static IEnumerable<TType> ScanProperties<TType, TValue>(this IEnumerable<TType> source, TValue value)
    {
        return ScanProperties<TType, TValue>(source, value, EqualityComparer<TValue>.Default);
    }

    public static IEnumerable<TType> ScanProperties<TType, TValue>(this IEnumerable<TType> source, TValue value, IEqualityComparer<TValue> comparer)
    {
        return source.Where(CreatePredicate<TType, TValue>(value, comparer));
    }
}

这将允许您执行以下操作:

var result = Items.ScanProperties("Filip").ToList();

答案 1 :(得分:3)

您可以使用表达式树来实时构建lambda:

Func<T, bool> CreatePredicate<T>()
{
    var arg = Expression.Parameter(typeof(T), "arg");
    var body = Expression.Equal(Expression.Property(arg, "Name"),
                                Expression.Constant("Filip"));
    return Expression.Lambda<Func<T, bool>>(body, arg).Compile();
}

IEnumerable<T> GetTWhereNameIsFilip<T>(IEnumerable<T> source)
{
    Func<T, bool> predicate = CreatePredicate<T>();
    // cache predicate for max performance

    return source.Where(predicate);
}