How do I subscribe to an event of an object inside

2019-07-27 05:22发布

问题:

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

回答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()();
    //...
}