.NET 2.0 started to apply a somewhat obscure programming concept called contravariance to delegates. Delegate contravariance, as what it’s being referred to, allows you to hook a method to a delegate which parameter is a descendant of the parameter in the method. If you’re confused, here’s what it means:
In the snippet above, you are able to hook OnAnyEvent even though the CancelEventHandler is defined with CancelEventArgs. This is because CancelEventArgs is a descendant of EventArgs. The EventArgs, under the virtue of polymorphism, can accept the CancelEventArgs passed by the TextBox control.
Where is it?
Delegate contravariance is not apparent in Visual Studio which, in my opinion, mainly contributed to its obscure nature and low adaption. If you hook an event to an existing handler at design time, you're shown only those which signature matches the event. In our example, you won’t see the OnAnyEvent contravariant delegate at all.
If you hook manually on the other hand, you’re provided hints which are not helpful either.
So the only way to apply delegate contravariance in your code is by brute force. Unless you have explicit knowledge about it, you would never know it’s there. And even to those who are already enlightened, the manual coding still puts them off.
Should you care?
If your codes perform the same routine in different events regardless of the event signature, the decision is no-brainer. I think this is contravariance raison d’ etre. But the thing is, this scenario is very rare. More often than not, you hook to an event because you wanted to execute specific routines and take advantage of the context properties. In our example, we hook to TextBox validating event to validate inputs and control the behavior of the focus using the CancelEventArgs.Cancel property. The question then becomes: Is it worth hooking more than one handler to a particular event to handle the generic and specific routines separately? My answer is NO. Additional handler is additional awareness layer. This practice compels you to always think that something else is hooking to an event, therefore complicating the debugging process. I rather convert the generic handler to a method which I can call within the other handler as shown here:
Should you adapt multi-handler approach thru contravariance, just remember that the order of calls follows the order of hooks. So if you wanted to fire the generic handler prior to the specific, you have no other choice but to hook both handlers by code because as you’ve seen a while ago, VS does not allow hooking of contravariant handler let alone 2 handlers, at design time. This necessitates slight modifications in the constructor as shown here:
The good thing is that the EventArgs hierarchy is still very shallow. My guess is it’s only two to three levels deep. So most probably you would only hook at most 2 or 3 handlers per event if you follow the event handler-decomposition path to cohesiveness. Still this is not a guard to bad practices because the very nature of .NET event does not prevent anyone from hooking handler to her heart’s content.
So what’s the next move?
My opinions on delegate contravariance are limited only to event-handling so I’m not going to conclude anything about its true merit. I’m still rummaging my old Windows application hoping to find sections I can refactor using this concept. Delegates are all over the place and viewing this concept only within the confine of UI programming is myopic. I’m sure I will find good uses of it nowadays that LINQ and lambda’s are starting to gain momentum.
No comments:
Post a Comment