The reason is performance (actually, that's my best guess). Events and delegates are made with a lot of compiler magic, stuff that cannot be replicated by mere C# code. But underneath it goes something like this:
A delegate is a compiler-generated class that inherits from MulticastDelegate
class, which itself derives from the Delegate
class. Note these two classes are magic and you can't inherit from them yourself (well, maybe you can, but you won't be able to use them very well).
An event however is implemented something like this:
private MyEventDelegateClass __cgbe_MyEvent; // Compiler generated backing field
public event MyEventDelegateClass MyEvent
{
add
{
this.__cgbe_MyEvent = (MyEventDelegateClass)Delegate.Combine(this.__cgbe_MyEvent, value);
}
remove
{
this.__cgbe_MyEvent = (MyEventDelegateClass)Delegate.Remove(this.__cgbe_MyEvent, value);
}
get
{
return this.__cgbe_MyEvent;
}
}
OK, so this isn't real code (nor is it exactly the way it is in real life), but it should give you an idea of what's going on.
The point is that the backing field initially really is null. That way there is no overhead for creating an instance of MyEventDelegateClass
when creating your object. And that's why you have to check for null before invoking. Later on, when handlers get added/removed, an instance of MyEventDelegateClass
is created and assigned to the field. And when the last handler is removed, this instance is also lost and the backing field is reset to null again.
This is the principle of "you don't pay for what you don't use". As long as you don't use the event, there will be no overhead for it. No extra memory, no extra CPU cycles.