Passing Strongly Typed Events As Arguments

I know there is a lot of people that are using LambdaExpression trees to pass around to use when dealing with INotifyPropertyChanged where it gets rid of magic string, which is cool.

Things I had this requirement the other day where I wanted to pass around an Events in a strongly typed manner, and this seems to be something that not that many people want to do. So I put my thinking cap on and this is what I came up with.

It doesn’t use Expression trees though, as Expression trees do not allow the use of “+=” or “-=”.

We can however use a neat trick where we use .NET Remoting Proxy (or any other Proxy such as LinFu or Castle DP) to intercept a call to Add/Remove handler on a very short lived proxy object. The role of this proxy object is to simply have some method called on it, and to allow its method calls to be intercepted, at which point we can find out the name of the event.

This sounds weird but here is the code (which by the way ONLY works if you have a MarshalByRefObject or an interface for the proxied object)

Assume we have the following interface and class

interface ISomeClassWithEvent
{
    event EventHandler<EventArgs> Changed;
}

public class SomeClassWithEvent : ISomeClassWithEvent
{
    public event EventHandler<EventArgs> Changed;

    protected virtual void OnChanged(EventArgs e)
    {
        EventHandler<EventArgs> handler = Changed;
        if (handler != null)
            handler(this, e);
    }
}

Then we can have a very simply class that expects an Action delegate that will get passed some instance of T.

Here is the code

public class EventWatcher<T>
{
    public void WatchEvent(Action<T> eventToWatch)
    {
        CustomProxy<T> proxy = new CustomProxy<T>(InvocationType.Event);
        T tester = (T)proxy.GetTransparentProxy();
        eventToWatch(tester);

        Console.WriteLine(string.Format("Event to watch = {0}", proxy.Invocations.First()));
    }

}

The trick is to pass the proxied object to the Action delegate provided.

Where we have the following CustomProxy code, who intercepts the call to += and -= on the proxied object

public enum InvocationType { Event }

public class CustomProxy<T> : RealProxy
{
    private List<string> invocations = new List<string>();
    private InvocationType invocationType;

    public CustomProxy(InvocationType invocationType)
        : base(typeof(T))
    {
        this.invocations = new List<string>();
        this.invocationType = invocationType;
    }

    public List<string> Invocations
    {
        get { return invocations; }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
    [DebuggerStepThrough]
    public override IMessage Invoke(IMessage msg)
    {

        String methodName = (String)msg.Properties["__MethodName"];
        Type[] parameterTypes = (Type[])msg.Properties["__MethodSignature"];
        MethodBase method = typeof(T).GetMethod(methodName, parameterTypes);

        switch (invocationType)
        {
            case InvocationType.Event:
                invocations.Add(ReplaceAddRemovePrefixes(method.Name));
                break;
            //YOU COULD DEAL WITH MORE CASES HERE IF YOU WANT TO
        }

        IMethodCallMessage message = msg as IMethodCallMessage;
        Object response = null;
        ReturnMessage responseMessage = new ReturnMessage(response, null, 0, null, message);
        return responseMessage;
    }

    private string ReplaceAddRemovePrefixes(string method)
    {

        if (method.Contains("add_"))
            return method.Replace("add_", "");
        if (method.Contains("remove_"))
            return method.Replace("remove_", "");
        return method;

    }
}

And then we all that’s left is to use this as follows

class Program
{
    static void Main(string[] args)
    {
        EventWatcher<ISomeClassWithEvent> eventWatcher = new EventWatcher<ISomeClassWithEvent>();
        eventWatcher.WatchEvent(x => x.Changed += null);
        eventWatcher.WatchEvent(x => x.Changed -= null);
        Console.ReadLine();
    }
}

Doing this I will see this output:

Event to watch = Changed
Event to watch = Changed

This worked fine for me. Enjoy

As always here is a small demo project  : http://dl.dropbox.com/u/2600965/Blogposts/2012/06/PassingStronglyTypedEventNames.zip

Advertisements

One thought on “Passing Strongly Typed Events As Arguments

  1. Richard says:

    Rather than digging in to the Properties dictionary and then using reflection to get the method, you could use the MethodBase property on the IMethodCallMessage instance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: