1

Sorry I couldn't think of a better title. This is a two part question that only make sense together.

Say I have a constructor like this

public Fact(INotifyPropertyChanged observable, Func<bool> predicate)
{
    this.predicate = predicate;
    observable.PropertyChanged += (sender, args) =>
                     PropertyChanged(this, new PropertyChangedEventArgs("Value"));

}

and this is how it's used

new Fact(Model.AllowEditing, () => Model.AllowEditing);

where AllowEditing is a type of INotifyPropertyChanged

I would like to refactor the constructor into

public Fact(Expression<Func<bool>> expression)

So it can be call like this

new Fact(() => Model.AllowEditing);

The question is how to parse that expression to get "observable" out of the expression tree then subscribe to its event?

The code above is not mine, it come from an example recent example from Ayende, here is the like to the full source code if anyone want to take a look of how the Fact class is being used http://github.com/ayende/Effectus

skaffman
  • 398,947
  • 96
  • 818
  • 769
firefly
  • 285
  • 3
  • 12

1 Answers1

1

Basically, the object you need in this case is stored in expression.Body.Expression. You will need to compile and execute an expression tree to get it. Also, you will probably need to check whether the type really implements your interface and whether the predicate is what your really are looking for.

Here is a very rough code:

public Fact(Expression<Func<bool>> predicate)
{        
    this.predicate = predicate;
    MemberExpression body = predicate.Body as MemberExpression;

    // Check that you get a valid lambda expression, 
    // like object.Member, not something like !object.Member
    if (body == null) 
         throw new ArgumentException("'" + predicate + "': is not a valid expression for this method");
    var type = body.Expression.Type;

    // Check if type implements INotifyPropertyChanged using reflection  
    // ...

    INotifyPropertyChanged observable = 
    Expression.Lambda<Func<INotifyPropertyChanged>>(body.Expression).Compile()();
    //...
}
Alexandra Rusina
  • 10,991
  • 2
  • 20
  • 16
  • the predicate doesn't come in as a MemberExpression, it's UnaryExpression. So I play around with it but still couldn't get to work. I like your EExpression.Lambda>(body.Expression).Compile()(); That's some serious code to wrap my head around. – firefly Dec 23 '09 at 16:32
  • Hm... How can it be UnaryExpression? To get a unary expression, you need to supply a lambda expression with a unary operation. For example, !(Model.AllowEditing), -Model.AllowEditing or Model.AllowEditing++. But this is what I meant by "check whether the predicate is what you are really looking for". Basically, you want to accept only MemberExpression here, and if "body" is null you should just throw an exception like "InvalidArgument". As for Compile()(), just be aware that in fact it is a heavy operation. Don't overuse it. – Alexandra Rusina Dec 23 '09 at 18:16
  • I am not sure why, my knowledge in this area is still limited. Though that's what reported to me in the debugger. – firefly Dec 25 '09 at 16:40
  • Can you post the exact code where you call this constructor? I bet it looks like new Fact(() => !Model.AllowEditing); – Alexandra Rusina Dec 29 '09 at 22:25
  • Yes, I call it like this new Fact(() => Model.AllowEditing); However it's worth pointing out that AllowEditing is not bool, It come from a class wrapper class Obserable so it's Obserable. The code for the class can be found here if you have time to take a look http://github.com/ayende/Effectus Sorry I had been a little busy over the course few weeks. I really appreciate your help. – firefly Jan 12 '10 at 12:35
  • I'd say, just use an expression tree visualizer and see what is actually inside your expression and then figure out how to get the MemberExpression you need. VS 2010 has a built-in visualizer (http://blogs.msdn.com/csharpfaq/archive/2009/11/19/debugging-expression-trees-in-visual-studio-2010.aspx). For 2008, there is sample: http://msdn.microsoft.com/en-us/library/bb397975.aspx. The code you refer to is simply too long and hard for me to analyze at a glance. – Alexandra Rusina Jan 12 '10 at 21:05
  • Thanks to your pointer I figure it out :). Sorry I had been extremely busy for the past few month. – firefly Feb 16 '10 at 09:24