3

tl;dr: Why are field-like events implemented as a single delegate field? Wouldn't it be more straight-forward to use a list of delegates, thereby eliminating the null special case and avoiding all the MulticastDelegate magic?


The usual pattern to raise field-like events (ignoring synchronization issues for the moment) is

if (MyEvent != null)
    MyEvent(this, e);

which can be quite confusing (Why do I have a null check for 0 handlers, but no loop for multiple handlers?), until you understand how field-like events are implemented and that MyEvent refers to an automatic backing field which can contain either null, a single delegate or a MulticastDelegate holding references to multiple handlers.

On a first glance, this seems to be a strange design choice. In particular, the obvious alternative would be to use a list of simple delegates, which is how the Observer pattern is commonly implemented in languages such as Java. True, the invocation would be slightly longer:

foreach (var handler in MyEvent)
    handler(this, e);

but it would be easier to understand (in my subjective opinion) and probably easier to implement (no need for all the delegate multicasting logic).

Obviously, the C# designers thought otherwise. Since they are a bunch of very smart people, there must have been a good reason for it. What is it?

migrated from stackoverflow.com Mar 16 '15 at 16:20

This question came from our site for professional and enthusiast programmers.

  • The question should be about delegates in .NET, not about events. Why is there not a delegate of each delegate type (e.g. Action<Args>) whose invocation list is the empty list? They chose that delegate removal (x minus y for delegates) will give null instead of an instance with empty invocation list in the case where the last item(s) in the list disappears by the removal. Why? – Jeppe Stig Nielsen Mar 15 '15 at 23:13
  • 1
    @JeppeStigNielsen: At least with C# 6.0 and the Elvis operator, the null check won't be necessary anymore: MyEvent?.Invoke(this, e); – Heinzi Mar 20 '15 at 12:12
1

All delegates are effectively lists. The empty list is null, but you can still append to it with +=.

The need to check for null before calling is very inconvenient, yes. But does not occur because they are not lists! It occurs because of how the empty list is represented.

However you can avoid this by initialising the event:

public event EventType MyEvent = delegate {};

See also: https://stackoverflow.com/questions/170907/is-there-a-downside-to-adding-an-anonymous-empty-delegate-on-event-declaration?rq=1

  • Yes, that's exactly my question: Why did they choose a (custom) list representation with an utterly inconvenient 0-element case instead of a (default) list with a convenient 0-element case? – Heinzi Mar 15 '15 at 22:37
  • Not the only option available: they could have allowed calling null delegates to succeed but do nothing (and return default(T) for delegates with non-void return type). I think VB.NET did something like that. But the C# team thought it was too magical. – Daniel Earwicker Mar 15 '15 at 23:41
1

A common use for events is in form controls. A form control can have a lot of events, a ListView for example has 79 different events.

Having to create a delegate list for every event would mean that there would be a lot of those. With just a dozen controls there would be around a thousand delegate lists to create, but most of those would end up unused and only taking up space.

There is a clear advantage to have a more light-weight way of representing an unused event. By using a null reference for that there is no extra initialisation needed for the events, as objects fields are automatically set to their default value.

Similarly there is an advantage of having separate single-cast and multi-cast events, and move the responsibility for calling the delegates into those classes. An event doesn't have to be a list when there is only one subscriber to it (which is the most common case), which makes the object smaller and the calling process simpler.

  • But types like ListView don't use field-like events. For them, even those 79 nulls would be too much. – svick Mar 15 '15 at 23:50
  • @svick: I don't follow what you mean? – Guffa Mar 16 '15 at 0:06
  • The question asks why do field-like events look the way they do. Your answer talks about ListView, which has lots of events, but at least some of them are implemented using EventHandlerList. So, how empty delegate looks like doesn't matter for those events, because empty delegates are never stored. – svick Mar 16 '15 at 0:13
  • 1
    The problem of creating all of the empty lists can be easily solved with a "null object pattern" -- make a singleton object for each delegate type which is the "null" delegate. But you still have the problem of initializing all those fields to the "null object". That initialization is either potentially expensive, if done automatically, or error-prone, if done manually. – Eric Lippert Mar 23 '15 at 22:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.