1

I'm writing something in the flavour of Enumerable.Where in that takes a predicate of the form Func<T, bool>. If the underlying T implements INotifyPropertyChanged, I'd like to be a bit more intelligent about re-evaluating the predicate.

I'm thinking about changing it to use Expression<Func<T, bool>>, and then using the expression tree to find out which properties are used in the predicate. Then I can have my PropertyChanged handler be a bit more intelligent.

My question: is this feasible? If the predicate's simple (e.g. x => x.Age > 18), then the Expression seems to have everything I need in it. Are there scenarios where I won't be able to see which properties are referenced?

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380

2 Answers2

2

Yes, you'll be able to see everything directly referenced. Of course, if someone passes

x => ComputeAge(x) > 18

then you won't necessarily know that ComputeAge refers to the Age property.

The expression tree will be an accurate representation of exactly what's in the lambda expression.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Cool, so if it's a simple expression, I'm good. If it's a complex expression, I'll have to fall back onto blindly re-evaluating the predicate. – Roger Lipscombe Sep 08 '10 at 11:55
  • @Roger: Even if it only contains properties, you need to consider whether those properties will refer to others. For example, suppose the `Age` property uses the `DateOfBirth` property... then a change to `DateOfBirth` should trigger re-evaluation, but the expression won't directly expose that dependency :( – Jon Skeet Sep 08 '10 at 11:59
  • Bah, so the answer (for my purposes) is "No: you can't reliably see which properties are used". Fair enough: blind re-evaluation is the solution then. – Roger Lipscombe Sep 09 '10 at 08:05
1

Small code example of a visitor that would find directly referenced properties.

public class PropertyAccessFinder : ExpressionVisitor {
    private readonly HashSet<PropertyInfo> _properties = new HashSet<PropertyInfo>();

    public IEnumerable<PropertyInfo> Properties {
        get { return _properties; }
    }

    protected override Expression VisitMember(MemberExpression node) {
        var property = node.Member as PropertyInfo;
        if (property != null)
            _properties.Add(property);

        return base.VisitMember(node);
    }
}

// Usage:
var visitor = new PropertyAccessFinder();
visitor.Visit(predicate);
foreach(var prop in visitor.Properties)
    Console.WriteLine(prop.Name);
sisve
  • 19,501
  • 3
  • 53
  • 95