patterncsharpModerate
Observable Object Proxy
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
In my scenario, I'll use the proxy to intercept calls to a
This object, as you've seen, is
```
public interface IObservableObjectFactory
{
T Wrap(T obj) where T : MarshalByRefObject;
T C
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:
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.
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
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 toSubstring(4)which presumably refers to the text afterSetterMethodStart, but this should then be the length of this variable in case it got changed or someone decides to use a more commonsetVariableNamestructure in their code.
- Is
SetterMethodStartnamed 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 useSETTER_METHOD_STARTor_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.