Objective
I needed a component that will hook into any event raised on any objects, to keep track of the number of events raised. Simple, huh?Hardwired Proof of Concept
My first attempt was in the constructor of the observed object, that registered its events to be handled by a CountEvent method in an EventMonitor object.It worked:
But it seriously ignored SRP in that the observed object shouldn't care how its events are handled.
Refactor I - The EventHandler Parameter
With SRP in mind, I created a RegisterEvent method to accept the object's event, so that the monitor could add its own handler - there still needed to be knowledge of observer and observed, but that could be handled in a builder object so no harm there. A nice side effect of this is that the method that actually handled the event (CountEvent) could be made private. This is the EventMonitor code:Lovely. Next, time to implement the new RegisterEvent in the client:
Easy. Now, time to run it:
Hmm. I'm not entirely sure what happened there, but it appears that you can't just go passing events around like that as it doesn't give you access to the Add and Remove handlers that you need to call (using the += and -= syntax). Note to self: Jon Skeet will know.
The ref Keyword is Your Friend
However, a quick addition of the ref keyword seemed to work:The Builder
Excellent. Next, I wanted to totally remove the responsibility of event registration from the observed object. Enter, the builder object:You'll notice that the event passed as the argument to RegisterEvent is in an error state. The error that this produced is "The event 'ObservedObject.ObservedEvent' can only appear on the left hand side of += or -= (except when used from within the type 'ObservedObject')"
It worked
What's That Smell?
Lets revisit the objective of the exercise: I need a component that will hook into any event raised on any objects, to keep track of the number of events raised.
Clearly I didn't have this yet - I'd merely abstracted the construction of one object into a builder class. All good, but a long, long way from any event on any object.
Reflection
As we know, nothing is really safe from prying eyes in the .Net world; every object is available to be decomposed via reflection. And if you can't beat them, join them... I found an article on MSDN, How to: Hook Up a Delegate Using Reflection. This explained how to use reflection to discover an object's events, and crucially how to create a delegate and use it to handle the object's events.
Here's the final code for the event monitor:
Here's an extended ObservedObject, with a standard and custom eventhandler:
And the builder. Note, this just passes the ObservedObject through to the EventMonitor, ensuring that if the ObservedObject changes by adding events, its builder doesn't need to.
GitHub
The final version of the EventMonitor example is available to inspect here on GitHub
No comments:
Post a Comment