HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Observable Object Proxy

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
objectproxyobservable

Problem

As a part of my (future) MVVM framework, I need to be able to detect changes on an object. To do this, I used the RealProxy, a C# object that implements Aspect Oriented Programming to "intercept" calls to an object's method.

In my scenario, I'll use the proxy to intercept calls to a _target's setter to launch an event, PropertyChanged.

public delegate void PropertyChanged(PropertyChangedArgs args);

/// 
/// Proxy that intercepts call to methods
/// 
/// Type of the target
internal class ObservableObjectProxy : RealProxy where T : MarshalByRefObject
{
    private const string SetterMethodStart = "set_";
    private readonly T _target;

    public event PropertyChanged PropertyChanged;

    /// 
    /// Creates a new instance of ObservableObjectProxy
    /// 
    /// Object to proxy
    public ObservableObjectProxy(T target) : base(typeof(T))
    {
        if (target == null) throw new ArgumentNullException(nameof(target));

        _target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        if (methodCall == null)
            throw new NotSupportedException("Invocation must be done on a method");

        var result = InvokeMethod(methodCall);

        if (!IsSetterCall(methodCall))
            return result;

        var interceptedArgs = new PropertyChangedArgs(methodCall.MethodName.Substring(4),methodCall.GetArg(0));

        PropertyChanged?.Invoke(interceptedArgs);

        return result;
    }

    private IMethodReturnMessage InvokeMethod(IMethodCallMessage callMsg)
    {
        return RemotingServices.ExecuteMessage(_target, callMsg);
    }

    private static bool IsSetterCall(IMethodCallMessage method)
    {
        return method.MethodName.StartsWith(SetterMethodStart);
    }
}


This object, as you've seen, is internal. To create it, I'm using a factory.

```
public interface IObservableObjectFactory
{
T Wrap(T obj) where T : MarshalByRefObject;
T C

Solution

Are you reinventing the wheel?

I'm sorry if this addresses just a little part of your question, but if you're looking for a way to get an event when a property is changed, have you thought about implementing INotifyPropertyChanged (msdn)?

With this you could do stuff like:

public class Person : INotifyPropertyChanged 
{
    ...
    public string PersonName 
    {
        get { return name; }
        set { name = value; OnPropertyChanged("PersonName"); }
    }
}


And with some accompanying stuff you'll get event handling almost for free. I've also seen a similar pattern, but can't find it just now, where you set the new value as part of the property change handling.

In other words, you might have reinvented the wheel, and on the flip side I might have misunderstood your question.

A few style comments

Here goes for a more proper review of your code, not focusing on the reinvention which you seems to want to do.

  • Magic number within Invoke > var interceptedArgs = ... - You do a call to Substring(4) which presumably refers to the text after SetterMethodStart, but this should then be the length of this variable in case it got changed or someone decides to use a more common setVariableName structure in their code.



  • Is SetterMethodStart named according to conventions? – According to this you are correctly naming it like you've done, but as this is extremely similar to the MethodNames I'm inclined to suggesting to use SETTER_METHOD_START or _SetherMethodStart. But then again that is kind of against the guidelines...



How do you use it?!

The last point I would like to make, is that I still don't quite get how you want to practically use this. And neither do I quite understand how you separate from get notified on all changes to any property or what/where your filter code is.

As such, there might be something said regarding useful comments within your code describing use cases and the point of using your class. The code and comments you have now are somewhat obvious and at the same time unclear as to the real purpose of your code.

A typical example is "Proxy that intercepts call to methods" which kind of says what it does, but then again why does it do that, and what is the purpose of doing this? Maybe a better comment would be "Intercepts calls to setter methods (a.k.a. methods prefixed with set_), and for these invokes PropertyChanged.Invoke with arguments`"

Code Snippets

public class Person : INotifyPropertyChanged 
{
    ...
    public string PersonName 
    {
        get { return name; }
        set { name = value; OnPropertyChanged("PersonName"); }
    }
}

Context

StackExchange Code Review Q#113430, answer score: 10

Revisions (0)

No revisions yet.