Event definition
To ease event management in .NET, Microsoft strongly recommends to define events according the following pattern:
event EventHandler<TEventArgs> Event;
All you have to define is an own TEventsArgs class that is inherited from EventArgs. This is done to ease the maintainability of events, it is likely that in future, the class gets enhanced. If you would not follow the pattern, each time you change the event, you would have a breaking API change. Reason: You have to add additional parameters to your delegate. That breaks existing code. This pattern prevents that breaking API change.
The TEventArgs class should be named after the event. The event itself should not contain "Event" within its class name.
So, a concrete example would be:
event EventHandler<ChangedEventArgs> Changed;
Event raising
In .NET, an event is some synatic sugar to a list of delegates. In case no one is registered to an event, that event simply returns null. This is by design and will not be changed by Microsoft.
So, before you can raise an event, you have to check for null. But caution, each time you access the event, you will get a new list of delegates. This is by design and will not be changed, too. Hence, the following code contains a race condition that is hard-to-debug:
if (Changed != null)
{
Changed(sender, new ChangedEventArgs(someData));
}
To avoid that situation, you have to store the delegates into a local variable and invoke that.
var handlers = Changed;
if (handlers != null)
{
handlers(sender, new ChangedEventArgs(someData));
}
Hence, each time you try to raise an event, you have to keep that in mind. This leads to a lot of duplicated code and is error-prone.
To avoid such situation, we can define and use an extension method. That method ensures that the race-condition is gone and, as we can use it everywhere, no mistake can be done.
The extension method (I've put it into our "Common" project) will look like:
public static void RaiseWith<TEventArgs>(this EventHandler<TEventArgs> handlers, object sender, TEventArgs e) where TEventArgs : EventArgs
{
if (handlers != null)
{
handlers(sender, e);
}
}
Now, we can raise an event easily the following way:
Changed.RaiseWith(sender, new ChangedEventArgs(someData));
Event documentation
Each event should have the following API comment:
/// <summary>
/// <para>
/// Occurs ....
/// </para>
/// </summary>
Example:
/// <summary>
/// <para>
/// Occurs immediately after the data has changed.
/// </para>
/// </summar>
event EventHandler<ChangedEventArgs> Changed;
Each EventArgs class should have following API comment:
/// <summary>
/// <para>
/// Provides data for the <see cref="..."/> event.
/// </para>
/// </summary>
Example:
/// <summary>
/// <para>
/// Provides data for the <see cref="IExample.Changed"/> event.
/// </para>
/// </summary>
public class ChangedEventArgs : EventArgs
Each event handling method should have the following API comment:
/// <summary>
/// <para>
/// Handles the <see cref="..."/> event by ....
/// </para>
/// </summary>
/// <param name="sender">
/// <para>
/// The source of the event.
/// </para>
/// </param>
/// <param name="e">
/// <para>
/// A <see cref="..."/> that contains the event data.
/// </para>
/// </param>
Example:
/// <summary>
/// <para>
/// Handles the <see cref="IExample.Changed"/> event by ....
/// </para>
/// </summary>
/// <param name="sender">
/// <para>
/// The source of the event.
/// </para>
/// </param>
/// <param name="e">
/// <para>
/// A <see cref="ChangedEventArgs"/> that contains the event data.
/// </para>
/// </param>
private void HandleEventIExampleChanged(object sender, ChangedEventArgs e)