views:

546

answers:

5

So what I'm trying to do is call a single propertyWasSet() function when any property within a C# class is set (conversely, propertyWasGot() when it is get). I would also like to know which property's 'get' was invoked.

I would like to maintain a dictonary of properties that are 'set', and check upon the 'get' action if they have been set yet (and throw an error if it hasn't been).

I've be looking through msdn documentation for reflection, delegates, etc..., but I'm not entirely sure this is possible.

Is there a way to do this? or fire an event upon calling one of these functions that can be intercepted in a base class or something?

A: 

I think what you need is very similar to WPF dependency property system. You might want to look at its implementation. Anyhow, you can add the intercepting code to getter and setter of each property too.

Mehrdad Afshari
yeah, I'm trying to avoid adding checking to every property as there are several, and the code is the almost the same for each.
+3  A: 

You may want to look into PostSharp for this sort of task. It is designed to run on top of C# (or any .NET language for that matter) and has the benefit of not cluttering up your code with reflection in addition. In fact, I don't believe you could find a solution that purely uses C#/Reflection without manually adding code to each of your properties, so I would definitely recommend PostSharp as the way to go.

Noldorin
A: 

There's nothing like this if you don't create it yourself.

John Saunders
I considered creating a new class at runtime, and wrapping all class properties in a method that does stuff, then goes about its normal business, but again, I'm not sure if this is possible, and it gets complicated quickly.
+2  A: 

I wrote an interceptor the other week for Set which can easily be extended for Get, it uses RealProxy, which means your base class needs to derive off MarshalByRefObject.

Another fancy option is to have your class abstract, and use Reflection Emit to construct a concrete class that wraps up all the properties.

Also you could look at code generators to get around this or PostSharp...

Performance for this solution is not stellar, but it should be plenty fast for most UI binding. It could be improved by generating LCG methods for proxy invocation.

public interface IInterceptorNotifiable {
    void OnPropertyChanged(string propertyName);
}

/// <summary>
/// A simple RealProxy based property interceptor
/// Will call OnPropertyChanged whenever and property on the child object is changed
/// </summary>
public class Interceptor<T> where T : MarshalByRefObject, IInterceptorNotifiable, new() {

    class InterceptorProxy : RealProxy {
        T proxy; 
        T target;
        EventHandler<PropertyChangedEventArgs> OnPropertyChanged;

        public InterceptorProxy(T target)
            : base(typeof(T)) {
            this.target = target;
        }

        public override object GetTransparentProxy() {
            proxy = (T)base.GetTransparentProxy();
            return proxy;
        }


        public override IMessage Invoke(IMessage msg) {

            IMethodCallMessage call = msg as IMethodCallMessage;
            if (call != null) {

                var result = InvokeMethod(call);
                if (call.MethodName.StartsWith("set_")) {
                    string propName = call.MethodName.Substring(4);
                    target.OnPropertyChanged(propName);
                } 
                return result;
            } else {
                throw new NotSupportedException();
            } 
        }

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

    }

    public static T Create() {
        var interceptor = new InterceptorProxy(new T());
        return (T)interceptor.GetTransparentProxy();
    }


    private Interceptor() {
    }

}

Usage:

  class Foo : MarshalByRefObject, IInterceptorNotifiable {
        public int PublicProp { get; set; }
        public string lastPropertyChanged;

        public void OnPropertyChanged(string propertyName) {
            lastPropertyChanged = propertyName;
        }

    }


    [Test]
    public void TestPropertyInterception() {

        var foo = Interceptor<Foo>.Create();
        foo.PublicProp = 100;

        Assert.AreEqual("PublicProp", foo.lastPropertyChanged);

    }
}
Sam Saffron
this is really interesting. I assume I could do the Create() stuff inside the constructor of the object though, so I wouldn't have to explicitly change the instantiation of all my objects.I am going to play with this. Thanks so much!
I think you are really going to need a factory for your objects, which can be a better practice anyway (since it allows you to swap object implementations in a later date and cache object instances)
Sam Saffron
A: 

The SET part of your request is very similar to WPF dependency property system. But the GET part is such unusual that is missing even in the WPF dependency property system!

M. Jahedbozorgan