views:

445

answers:

2

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:

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?

+10  A: 
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();
Adam Robinson
Thanks a lot, this is exactly what I am looking for. Just a side note, to make it a little bit more generic, how would you do if you want to apply a custom comparer? Ie if it is not a string you are looking for but you want you want to Compare two objects instead, or the objects properties. Is there a nice way to do that in this example?
Filip Ekberg
It might not be that hard to figure out, but since you gave such an excellent post you might have some thoughts about it. Thanks again.
Filip Ekberg
@Filip: The `IEqualityComparer<T>` interface is pretty straightforward; you subclass `EqualityComparer<T>` (strictly speaking you can implement the interface directly, but don't do this unless you specifically need to as there is built-in functionality in the base class) and override/implement the `Equals` function. It takes two parameters of the target type, and you return a boolean of whether or not they're equal. As to the particular logic involved in determining this, that's entirely up to you.
Adam Robinson
A very common case for a custom `IEqualityComparer` is for case-insensitive string comparisons on generic structures (like a `Dictionary`, for example). This allows you to specify functionality that might be intrinsic to the type you're comparing without making the class itself specific to that type. For example, you could optionally pass `StringComparer.CurrentCultureIgnoreCase` for the comparer parameter and that would match all items with a string property with "Filip" in any character casing.
Adam Robinson
+2  A: 

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);
}
dtb
The question stated that he wanted to search all of the public properties for the value.
Adam Robinson
@Adam Robinson: "at this time I do not care if the property-name is Name or not" -- maybe he cares later?
dtb
Is there a huge differnce in performance?
Filip Ekberg
Maybe, yes, but since he says "so that it searches All the public properties of the Type..." explicitly, I figured it was better to answer the question he asked rather than the one that he didn't ;)
Adam Robinson
@Filip Ekberg: There is a performance difference, as reflection is generally slow and expression trees, once compiled, are as fast as if you'd written the lambda directly. But I can't say how big the difference is. Use a Stopwatch! :-)
dtb