Lambda on List<T> and using Reflection to get the property names
Let's say you have a Generic Class which have a List<T> Items;
Now think of this basic lambda expression:
var result = Items.FindAll(x => x.Name = "Filip");
This will only work as long as we Know the Properties of T
, which you don't when it's a generic type.
Therefore I would like to fetch the properties using Reflection like this:
开发者_JS百科PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public);
and somehow combind that with the above Lambda-expression so that it searches All the public properties of the Type and see if it contains "Filip", at this time I do not care if the property-name is Name or not.
Is this possible?
var result = Items.FindAll(x =>
properties.Any(p => p.PropertyType == typeof(string) &&
p.GetValue(x, null) == "Filip"));
Obviously this is a simplistic, optimistic string comparison (you might want to use string.Compare
, for example), but this should make the idea clear.
Edit
dtb makes a good suggestion in using expression trees. You could accomplish what you're after in a faster fashion like this:
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));
}
}
This will allow you to do something like this:
var result = Items.ScanProperties("Filip").ToList();
You can use expression trees to construct a lambda on-the-fly:
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);
}
精彩评论