ObjectiveI 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 ConceptMy 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.
But it seriously ignored SRP in that the observed object shouldn't care how its events are handled.
Refactor I - The EventHandler ParameterWith 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:
Note to self: Jon Skeet will know.
The ref Keyword is Your FriendHowever, a quick addition of the ref keyword seemed to work:
The BuilderExcellent. 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')"
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.
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.
The final version of the EventMonitor example is available to inspect here on GitHub