C# delegates and events drawbacks
I am quite fond of events and delegates in C#. They are an interesting construct and have proven useful, especially in the winform and webform worlds. But in my recent uses of delegates in projects I have come to realize some drawbacks or at least limitations of delegates and events.
Naming conventions
The first one was a problem that I encountered when first learning about delegates, but that still bothers me now that I understand them: they are un-easy to name.
It might be stupid, but there is something about delegates and events that makes them hard to name. It may be because they combine object aspects with functional ones.
For example, let's say you have a delegate of type "bool foo(HttpRequest)". You could call your delegate RequestHandler, but then you have to choose between "requestHandler" and "handleRequest" for an instance of this delegate.
On one hand, "requestHandler" is closer to the common naming convention, but it feels weird to use it as a function, like "result = requestHandler(request)".
On the other hand, "handleRequest" feels nice when you invoke it, but seems un-natural when you instanciate it in "RequestHandler handleRequest = new RequestHandler(this.processRequest);".
I tried various naming conventions using suffixes (Event, Listener, Handler, Method and Function), but all created "semantic" friction at some point.
It may be because I am used to the Java-style, but to me "requestHandler" sounds like an object whereas "handleRequest" sounds like a method.
Java may add some cognitive load on the developper by requiring classes to implement delegate-like functionality, but it keeps the object and the method clear from each other. You can have an "EventListener" with a "handleEvent(Event)" method, whereas C# tries to merge both object and action semantics.
Delegates are still very useful when you need to handle many different similar events (like Clicks on a webform). I guess even this case could be handled in a Java-style webform, by writing listeners for each event or custom classes that would inherit the widgets and implement the event handling. But in any case you'd end up with tons of classes and the code probably wouldn't be clearer.
Extensibility
The second limitation is an issue I encountered recenly while implementing a plugin based P2P program. Since a delegate is pretty much an interface that has only one method, you are limited to one method, which limits the extensibility/flexibility of your design.
For example, you could have an "EventHandler" that only supports one method ("handleEvent"). It would make sense to use a delegate. But a couple days later you find out that you need another method for your event handlers, like say "IsEnabled()". If you had used an interface it'd be easy to add it, whereas with a delegate you just can't.
It may come down to good design and I wasn't able to make a good choice when I ran into this scenario. But it still raises the question: when should one use delegates rather than interfaces?
Links:
Ruby can do delegates too, using an interesting technique (something in between reflection and the prototypal behavior in javascript).
As an additional argument against delegates and in favor of interfaces, a delegate invocation takes 41ns, in comparison to 5.4ns for a virtual call, according to Jan Gray's analysis.
______________________________________I like Microsoft's convention of suffixing attribute objects with "Attribute"... [ISerializable] is actually an instance of ISerializableAttribute. I adopted that as my naming convention for delegates as well:
bool requestHandlerDelegate(HttpRequest);
requestHandlerDelegate myRequestHandler = new requestHandlerDelegate(this.processRequest);
Posted by: Addy Santo (September 25, 2003 01:56 PM) ______________________________________I like the "I" prefix for interfaces and the "Attribute" suffix for attributes too. But am still insatisfied by any suffix for delegates.
In your example, you'd end up writing:
myRequestHandler(myHttpRequest);
and that doesn't feel natural.
But that's pretty much what I use, because I don't think there is a better naming convention.
A solution to this naming problem would be to not have this implicit function call occur, but instead make it explicit, with "Invoke", "Call" or something similar:
myRequestHandler.Invoke(myHttpRequest);
This way you don't have objects that can be invoked like methods.
Posted by: Dumky (September 25, 2003 03:32 PM) ______________________________________I just got into blogging and I absolutely love it, so thanks, I keep track of this blog as well as 5 others so far.
Posted by: Bruce Parker (May 23, 2005 04:37 PM) ______________________________________In response to your "Naming Conventions" discussion, consider this:
Microsoft's convention for naming delegate types used for event handlers is to use the suffix "Handler", i.e. EventHandler, MouseEventHandler, BindingCompleteEventHandler, and ColumnClickEventHandler.
Generally, delegates used for other purposes seemed to be named as a noun which represents the action to perform. We use a noun because a delegate is a type of action, and a "type of action" is a thing. Action, TypeFilter, AppDomainInitializer, Comparison. They all seem to be nouns. Your http request handling delegate TYPE would be named RequestHandler, because the function it points to is a request handler.
I would not say that it looks funny invoking it, either. When you invoke it, you don't do it through the delegate type. You do it through a variable that is of the type of that delegate. Where the delegate type represents a "type of action," a noun, a delegate INSTANCE represents an action, which would be a verb. You can see this, for example, with events. System.EventHandler is our delegate type, which is the noun. Button.Click is an example of an instance of a delegate. Note that the identifier used here is a verb. Using your RequestHandler example, an instance of a RequestHandler would probably be named handleRequest, so in code you would see:
RequestHandler handleRequest = myRequestingObject.GetRequestHandler();
object result = handleRequest();
As far as your delegate versus interface issue, the problem seems to be that you aren't quite sure as to what a delegate is for. By far, the biggest use for delegates is event handling. An event always represents one action and only one callback will ever be needed. I don't quite understand your example, but any other time this is how I would decide. Simply put, a delegate is an action and an interface is an object that provides a particular service. Basically it comes down to the question "What is the essence of what your type represents?" Is it a singular simple action (and equally important, does a single function call represent the action from beginning to end)? Or is it a (potentially) more complex, functional object or a process that might endure longer than a function call?
For example, many .Net sort methods take a delegate of type Comprarer. A comparer does one simple, singular thing. It compares two objects. Start to finish with one function call. On the other hand, the foreach loop works via the IEnumerable interface under the hood. Enumeration is not as simple a process as comparison. You must return one object after another until all objects are returned. It is not a singular action, but a list of steps with OTHER LOGIC BETWEEN STEPS. You get an object, process it, then move to the next object and get that, process it, and so on.
The one criteria you DON’T want to use to determine whether to use an interface or a delegate is how many functions the action consists of. Just because the service an object provides consists of a single function doesn't mean that you should use a delegate. IDisposable performs disposal services. IComparable performs comparison services. IClonable performs cloning services. But they all only have one function.
Another thing you should consider is how you pass information in an event handler. Microsoft's event handlers don't have the event data as parameters, but rather objects that contain event data in parameters. If it is extensibility you want, you can extend the object that you pass to the event handler. It should be no problem to add a function to the object returned by an event handler. You want to know if something is enabled when you handle an event? Add an Enabled property to your EventArgs object. Want to be able to cancel the event? Add a cancel function (The winforms TreeView control's BeforeLabelEdit allows you to set a property to cancel the label edit).
I think I need to start my own blog. I have a lot to say. But I hope I helped disambiguate naming conventions and when to use what.