.NET Framework Developer's Guide 
Event Design 

Events are mechanisms that allow application-specific code to execute when an action occurs. Events happen either before the associated action occurs (pre-events) or after the action occurs (post-events). For example, when a user clicks a button in a window, a post-event is raised allowing application-specific methods to execute. An event-handler delegate is bound to the method to be executed when the system raises an event. The event handler is added to the event so that it is able to invoke its method when the event is raised. Events can have event-specific data (for example, a mouse down event can include data about the screen cursor's location).

The signature of the event-handling method is identical to the signature of the event-handler delegate. The event-handler signature observes the following conventions:

  • The return type is Void.

  • The first parameter is named sender and is of type Object. This is the object that raised the event.

  • The second parameter is named e and is of type EventArgs or a derived class of EventArgs.This is the event-specific data.

  • The method takes exactly two parameters.

For more information about events, see Handling and Raising Events.

Do use the raise terminology for events rather than fire or trigger.

Do use System.EventHandler<T> instead of manually creating new delegates to be used as event handlers.

This guideline mainly applies to new feature areas. If you are expanding the functionality in an area that already uses non-generic event handlers, you can continue to use non-generic event handlers to keep the design consistent.

You cannot comply with this guideline if your library targets versions of the .NET Framework that do not support generics.

Consider using a derived class of System.EventArgs as the event argument, unless you are absolutely sure the event will never need to carry any data to the event-handling method, in which case you can use the System.EventArgs type directly.

If you define an event that takes an EventArgs instance instead of a derived class that you define, you cannot add data to the event in later versions. For that reason, it is preferable to create an empty derived class of EventArgs. This allows you add data to the event in later versions without introducing breaking changes.

Do use a protected virtual method to raise each event. This is applicable only to non-static events on unsealed classes, not to structures, sealed classes, or static events.

Complying with this guideline allows derived classes to handle a base class event by overriding the protected method. The name of the protected virtual (Overridable in Visual Basic) method should be the same as the event name prefixed with On. For example, the protected virtual method for an event named "TimeChanged" is named "OnTimeChanged".

NoteImportant

Derived classes that override the protected virtual method are not required to call the base class implementation. The base class must continue to work correctly even if its implementation is not called.

Do use a parameter that is typed as the event argument class to the protected method that raises an event. The parameter should be named e.

The FontDialog class provides the following method, which raises the Apply event:

[Visual Basic]

Protected Overridable Sub OnApply( ByVal e As EventArgs )

[C#]

protected virtual void OnApply(EventArgs e);

Do not pass null (Nothing in Visual Basic) as the sender parameter when raising a non-static event.

On static events, the sender parameter should be null (Nothing in Visual Basic).

Do not pass null (Nothing in Visual Basic) as the event data parameter when raising an event.

If there is no event data, pass Empty instead of null.

Do be prepared for arbitrary code executing in the event-handling method.

Consider placing the code where the event is raised in a try-catch block to prevent program termination due to unhandled exceptions thrown from the event handlers.

Consider raising events that the end user can cancel. This applies only to pre-events.

If you are designing a cancelable event, use CancelEventArgs instead of EventArgs as the base class for the event data object e.

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

See Also



Community Content

Dave Sexton
Designing Thread-safe Events in C#

Thread-safety is important and should be considered when designing event members on types that may be accessed concurrently by multiple threads.

Thread-safe Pattern

In the 2.0 Framework, the C# compiler adds a MethodImplAttribute with the MethodImplOptions.Synchronized flag to event members in class definitions that don't have explicitly defined add and remove accessors.  The MethodImplOptions.Synchronized flag locks the current instance in each accessor.  Some developers believe that this breaks encapsulation since the object can also be locked externally, possibly causing deadlocks.  A better approach would be to encapsulate the locking mechanism completely by defining a private field on which to lock the event's accessors.  It is recommended to lock a read-only instance of System.Object.  By explicitly defining add and remove accessors, the event member can be synchronized using the private field and the C# compiler will omit the MethodImplAttribute completely:

   private readonly object TextChangedEventLock = new object();
private EventHandler TextChangedEvent;
    
   public event EventHandler TextChanged
{
add
{
lock (TextChangedEventLock)
{
TextChangedEvent += value;
}
}
remove
{
lock (TextChangedEventLock)
{
TextChangedEvent -= value;
}
}
}

 

The same mechanism used to synchronize the acccessors should also be used to synchronize the method that raises the event; however, care should be taken so that the event's delegate isn't invoked within the synchronization code, otherwise deadlocks can occur:

   protected virtual void OnTextChanged(EventArgs e)
{
EventHandler handler = null;
    
       lock (TextChangedEventLock)
{
handler = TextChangedEvent;
    
   	if (handler == null)
return;
}
    
       // Invoke delegate outside of lock
       handler(this, e);
}
   

Component Events

When declaring an event member on a type that derives from System.ComponentModel.Component, the inherited Event property provides an EventHandlerList that can be used to reduce the memory footprint of your class since you'll no longer have to declare a private field to back the event.  Controls use this model since they commonly have many events defined but only a few are actually used at runtime:

  private readonly object TextChangedEvent = new object();
   
  public event EventHandler TextChanged
{
add
{
lock (TextChangedEvent)
{
Events.AddHandler(TextChangedEvent, value);
}
}
remove
{
lock (TextChangedEvent)
{
Events.RemoveHandler(TextChangedEvent, value);
}
}
}
   
  protected virtual void OnTextChanged(EventArgs e)
{
EventHandler handler = null;
   
      lock (TextChangedEvent)
{
handler = (EventHandler) Events[TextChangedEvent];
}
   
      if (handler != null)
  	// Invoke delegate outside of lock
handler(this, e);
}
Tags : code

Dave Sexton
VS 2005 Code Snippets For C#

The following C# code snippet creates the canonical event member declaration and corresponding method for invocation.  EventHandler is used as the delegate.  The code produced by this snippet is thread-safe:

http://davesexton.com/cs/files/folders/snippets/entry23.aspx

More snippets for event member declarations can be found at:

http://davesexton.com/cs/files/folders/snippets/default.aspx  

including snippets that serialize event members for component-derived types and events that use the generic EventHandler<TEventArgs> delegate.

Tags : blogpost

Dave Sexton
Explain more

When I "raise an event that the end user can cancel":
which of the flowing methods is correct:
1) protected virtual voidOnApply(CancelEventArgs e);
2) protected virtual boolOnApply(CancelEventArgs e); // Return value: Cancel by user?

Edit: Dave Sexton

Always return void in your On* methods. The CancelEventArgs class has a boolean Cancel property that the caller can check to see if any of the event handlers elected to cancel the event. But note that using the standard event invocation mechanism (i.e., using the delegate like a method) all event handlers will be invoked, regardless of whether any of them set the Cancel property to true. However, you can iterate over the event handlers instead (see Delegate.GetInvocationList method) and then break the loop if any of the handlers set Cancel to true. Whether this is appropriate or not depends on the event's semantics.

Tags :

Page view tracker