Click here to Skip to main content
13,350,289 members (48,543 online)
Click here to Skip to main content
Add your own
alternative version


62 bookmarked
Posted 1 Jul 2008

Event Chain

, 1 Jul 2008
Rate this:
Please Sign up or sign in to vote.
Executing a multicast delegate to create an event chain that can be terminated by any handler in the chain


Recently I needed a multicast delegate (an event, in other words) that was smart enough to stop calling the delegates in the invocation list when one of the delegates handled the event. In other words, I needed an event chain that stopped once a delegate in the chain indicated that it handled the event. I did some brief searching for this but didn't find anything, which surprises me for two reasons. First, I figured someone would have implemented this, and second, I thought this was implemented in C# 3.0. Perhaps I didn't look hard enough. Regardless, I needed this implementation for C# 2.0 and the .NET 2.0 Framework because that's what my code base currently requires.

The Code

As part of a previous article about an event pool, I had some code that was borrowed from Juval Lowy and Eric Gunnerson's best practices in defensive event firing mechanism, and Pete O'Hanlon upgraded this code to .NET 2.0 a while back. The code here is a modification of the safe event caller.

Note: You will be amused though that I removed the try-catch block because I now feel that the exception should not be caught by this code but rather by the caller, especially since, in the safe event caller implementation, the exception was re-thrown!

The IEventChain Interface

The critical aspect of this implementation is the IEventChain interface which the argument parameter to the event signature must implement. The event signature follows the .NET standard practice Handler(object sender, EventArgs args) where the "args" property is derived from EventArgs and implements IEventChain.

/// <span class="code-SummaryComment"><summary></span>
/// The interface that the argument parameter must implement.
/// <span class="code-SummaryComment"></summary></span>
public interface IEventChain
  /// <span class="code-SummaryComment"><summary></span>
  /// The event sink sets this property to true if it handles the event.
  /// <span class="code-SummaryComment"></summary></span>
  bool Handled { get; set; }

This interface has one property, Handled, which the event sink can set to true if it handles the event. The event invocation method inspects this property after invoking the delegate to see if the method set this property to true.

The EventChainHandlerDlgt Definition

The application uses this generic definition to specify the multicast delegate.

/// <span class="code-SummaryComment"><summary></span>
/// A generic delegate for defining chained events.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><typeparam name="T">The argument type, must implement IEventChain.</typeparam></span>
/// <span class="code-SummaryComment"><param name="sender">The source instance of the event.</param></span>
/// <span class="code-SummaryComment"><param name="args">The parameter.</param></span>
public delegate void EventChainHandlerDlgt<T>
    (object sender, T arg) where T : IEventChain;

The generic type <T> specifies the argument type. A very nifty feature that provides compile-time type validation is the where T : IEventChain clause, that imposes a restriction on the generic type <T> that it must implement IEventChain.

An Example Argument Class

The following illustrates the most basic implementation of an argument class suitable for the delegate described above:

public class SomeCustomArgs : EventArgs, IEventChain
  protected bool handled;

  public bool Handled
    get { return handled; }
    set { handled = value; }

In the code download, you'll see that I added another property and field simply for testing that the same argument instance is passed to all methods in the invocation list (as one would expect.)

Defining The Event Signature

A typical event signature looks like this:

public static event EventChainHandlerDlgt<SomeCustomArgs> ChainedEvent;

(The event is static only because in my test code, it is part of the Main method which is static.)

The event signature of course defines the method handler signature as well. So, when we say:

ChainedEvent += new EventChainHandlerDlgt<SomeCustomArgs>(Handler1);

The handler signature is expected to match the delegate as well (an example):

public static void Handler1(object sender, SomeCustomArgs args)

The EventChain Class

The EventChain class is a static class that implements one method: Fire. The code comments explain the operation of this method, which really is quite simple.

/// <span class="code-SummaryComment"><summary></span>
/// This class invokes each event sink in the event's invocation list
/// until an event sink sets the Handled property to true.
/// <span class="code-SummaryComment"></summary></span>
public static class EventChain
  /// <span class="code-SummaryComment"><summary></span>
  /// Fires each event in the invocation list in the order in which
  /// the events were added until an event handler sets the handled
  /// property to true.
  /// Any exception that the event throws must be caught by the caller.
  /// <span class="code-SummaryComment"></summary></span>
  /// <span class="code-SummaryComment"><param name="del">The multicast delegate (event).</param></span>
  /// <span class="code-SummaryComment"><param name="sender">The event source instance.</param></span>
  /// <span class="code-SummaryComment"><param name="arg">The event argument.</param></span>
  /// <span class="code-SummaryComment"><returns>Returns true if an event sink handled the event,</span>
  /// false otherwise.<span class="code-SummaryComment"></returns></span>
  public static bool Fire(MulticastDelegate del, object sender, IEventChain arg)
    bool handled = false;

    // Assuming the multicast delegate is not null...
    if (del != null)
      // Get the delegates in the invocation list.
      Delegate[] delegates = del.GetInvocationList();

      // Call the methods until one of them handles the event
      // or all the methods in the delegate list are processed.
      for (int i=0; i<delegates.Length && !handled; i++)
        // There might be some optimization that is possible
        // by caching methods.
        delegates[i].DynamicInvoke(sender, arg);
        handled = arg.Handled;

    // Return a flag indicating whether an event sink handled
    // the event.
    return handled;

Note that this method returns a boolean indicating whether one of the event sinks in the chain flagged that it handled the event.


The screenshot above is the result of this code example (part of the download):

public static void Main()
  ChainedEvent += new EventChainHandlerDlgt<SomeCustomArgs>(Handler1);
  ChainedEvent += new EventChainHandlerDlgt<SomeCustomArgs>(Handler2);
  ChainedEvent += new EventChainHandlerDlgt<SomeCustomArgs>(Handler3);

  SomeCustomArgs args=new SomeCustomArgs();
  bool ret = EventChain.Fire(ChainedEvent, null, args);

public static void Handler1(object sender, SomeCustomArgs args)

public static void Handler2(object sender, SomeCustomArgs args)
  args.Handled = true;

public static void Handler3(object sender, SomeCustomArgs args)

The point of this code is that Handler3 never gets called because Handler2 indicates that it handles the event.

Again, note that all these methods are static only because it was convenient to code this all in the Main method.


This code is simple enough that I felt it warrants being put in the "Beginner" section, yet illustrates a useful technique that extends the multicast delegate behavior. And as I mentioned in the introduction, it really does surprise me that someone hasn't done this before, and perhaps better, so if anyone has any other references to other implementations, please post the link in the comments for this article.


There are some interesting things one can do with this code. First off, it might be possible to optimize the invocation (how, I'm not sure right now). Also, one can change the order in which the invocation list is called. One can reverse the order, it can be based on some priority that is defined by the handler, and so forth. The handlers could be called as worker threads. All sorts of interesting possibilities are available when one has access to the invocation list!


  • 1st July, 2008: Initial post


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Marc Clifton
United States United States
Marc is the creator of two open source projects, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website,, where you will find many of his articles and his blog.

Marc lives in Philmont, NY.

You may also be interested in...

Comments and Discussions

Questioncould u give me c#3.0's implement resource? Pin
Member 34159511-Jul-08 22:45
memberMember 34159511-Jul-08 22:45 
GeneralExcellent (and timely) Article Pin
RFelts9-Jul-08 9:54
memberRFelts9-Jul-08 9:54 

I'm also stuck on a .Net 2.0 code base (for probably another 2 years) on my current project. I've been working on building a clean MVC model (and now my head's hurting...) for the project's 2.0 iteration and needed a good way to route events in fancy ways. Some of them are single-handle, like your code, some are to-all-clients, and some are delegated method calls to all clients.

This is going to save me quite a bit of coding time, conceptually. Thanks for the good work.

GeneralGood one Pin
N a v a n e e t h3-Jul-08 17:54
memberN a v a n e e t h3-Jul-08 17:54 
GeneralRe: Good one Pin
Marc Clifton4-Jul-08 3:40
protectorMarc Clifton4-Jul-08 3:40 
GeneralRe: Good one Pin
N a v a n e e t h4-Jul-08 17:15
memberN a v a n e e t h4-Jul-08 17:15 
QuestionAm I missing something simple? Pin
tonyt3-Jul-08 9:21
membertonyt3-Jul-08 9:21 
AnswerRe: Am I missing something simple? Pin
Marc Clifton3-Jul-08 9:52
protectorMarc Clifton3-Jul-08 9:52 
GeneralReminds me of WPF RoutedEvents Pin
ivolved2-Jul-08 17:53
memberivolved2-Jul-08 17:53 
GeneralRe: Reminds me of WPF RoutedEvents Pin
ivolved3-Jul-08 6:11
memberivolved3-Jul-08 6:11 
GeneralGreat Job + My novel idea Pin
Josh Smith2-Jul-08 13:16
mvpJosh Smith2-Jul-08 13:16 
GeneralRe: Great Job + My novel idea Pin
ivolved2-Jul-08 17:47
memberivolved2-Jul-08 17:47 
GeneralRe: Great Job + My novel idea Pin
Josh Smith3-Jul-08 0:59
mvpJosh Smith3-Jul-08 0:59 
GeneralOptimization Pin
Daniel Grunwald1-Jul-08 23:47
memberDaniel Grunwald1-Jul-08 23:47 
GeneralRe: Optimization Pin
Marc Clifton2-Jul-08 5:40
protectorMarc Clifton2-Jul-08 5:40 
GeneralRe: Optimization Pin
Marc Clifton2-Jul-08 5:51
protectorMarc Clifton2-Jul-08 5:51 
GeneralRe: Optimization Pin
Daniel Grunwald2-Jul-08 6:50
memberDaniel Grunwald2-Jul-08 6:50 
GeneralRe: Optimization Pin
Marc Clifton2-Jul-08 6:59
protectorMarc Clifton2-Jul-08 6:59 
GeneralNice Pin
Bert delaVega1-Jul-08 18:44
memberBert delaVega1-Jul-08 18:44 
GeneralNot bad Pin
GSerjo1-Jul-08 14:03
memberGSerjo1-Jul-08 14:03 
GeneralRe: Not bad Pin
Marc Clifton2-Jul-08 5:38
protectorMarc Clifton2-Jul-08 5:38 
GeneralI like this Marc Pin
Sacha Barber1-Jul-08 12:46
mvpSacha Barber1-Jul-08 12:46 
GeneralTres cool Pin
Mustafa Ismail Mustafa1-Jul-08 10:49
memberMustafa Ismail Mustafa1-Jul-08 10:49 
GeneralRe: Tres Pin
Pete O'Hanlon1-Jul-08 11:03
mvpPete O'Hanlon1-Jul-08 11:03 
GeneralRe: Tres Pin
Mustafa Ismail Mustafa1-Jul-08 11:31
memberMustafa Ismail Mustafa1-Jul-08 11:31 
GeneralRe: Tres Pin
Marc Clifton1-Jul-08 12:34
protectorMarc Clifton1-Jul-08 12:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.180111.1 | Last Updated 1 Jul 2008
Article Copyright 2008 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid