111

If I set up multiple event handlers, like so:

_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;

what order are the handlers run when the event RetrieveDataCompleted is fired? Are they run in the same thread and sequentially in the order that are registered?

Phillip Ngan
  • 15,482
  • 8
  • 63
  • 79
  • 2
    The answer will be specific to the RetrieveDataCompleted event. If it has the default backing store of a multi-cast delegate, then yes "they run in the same thread and sequentially in the order that are registered". – HappyNomad Jul 15 '13 at 16:09

13 Answers13

148

Currently, they are executed in the order they are registered. However, this is an implementation detail, and I would not rely on this behavior staying the same in future versions, since it is not required by specifications.

Larry
  • 17,605
  • 9
  • 77
  • 106
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 1
    This is important. It's entirely possible for a future version of something to, e.g., execute different handlers on different threads. Do not create a dependency on this implementation detail! – Greg D Oct 29 '09 at 18:15
  • 1
    Executing on different threads is unlikely, since that would break compatibility drastically. However, a change of data structure for internal storage could change this behavior just as easily. – Reed Copsey Oct 29 '09 at 18:21
  • 9
    I wonder, why the downvotes? This is exactly true, and answers the question directly... – Reed Copsey Oct 30 '09 at 16:04
  • Further question: 7.2.4 in the C# language spec says "...the result of the (addition) operation is a new delegate instance that, when invoked, invokes the first operand and then invokes the second operand". Do I have the wrong end of the stick, or does this say that the first should always get called before the second? Ref: http://msdn.microsoft.com/en-us/library/aa691375%28VS.71%29.aspx – Rawling Feb 26 '10 at 14:55
  • 3
    @Rawling: That's for binary operator overload resolution - not event handling. This isn't the addition operator, in this case. – Reed Copsey Feb 26 '10 at 16:38
  • 1
    OK... still from the spec, how about here: http://msdn.microsoft.com/en-us/library/aa664605%28VS.71%29.aspx "Invocation of a delegate instance whose invocation list contains multiple entries proceeds by invoking each of the methods in the invocation list, synchronously, in order." The code excerpt also shows the delegates called in the order they're added. Event handlers are delegates, right? – Rawling Mar 01 '10 at 10:12
  • 4
    Ah, I see where I'm going wrong: "Event handlers are delegates, right?". I now know they're not. Have written myself an event that fires handlers in reverse order, just to prove it to myself :) – Rawling Mar 01 '10 at 10:56
  • 24
    To clarify, the order depends on the backing store for a particular event. The default backing store for events, multi-cast delegates, are documented as executing in registration order. This will not change in a future framework version. What may change is the backing store used for a particular event. – HappyNomad Jul 15 '13 at 16:17
  • the answer could be improved with pin-point references and quotes to either the language specification or the implementation design or both – n611x007 Jun 27 '14 at 11:02
  • 9
    Downvoted since it is factually incorrect on 2 points. 1) Currently they are executed in the order that the implementation of the *specific event* dictates - since you can implement your own add/remove methods for events. 2) When using the default event implementation via multi-cast delegates, the order is in fact required by specifications. – Søren Boisen Jul 01 '15 at 18:06
  • @HappyNomad This question is badly in need of a correct answer, since all the existing ones are incomplete. Your comments offer the most complete and correct answer, so you should consider adding an independent answer :) – Søren Boisen Jul 01 '15 at 18:09
60

The invocation list of a delegate is an ordered set of delegates in which each element of the list invokes exactly one of the methods invoked by the delegate. An invocation list can contain duplicate methods. During an invocation, a delegate invokes methods in the order in which they appear in the invocation list.

From here: Delegate Class

Philip Wallace
  • 7,905
  • 3
  • 28
  • 40
  • 2
    Nice, but using the `add` and `remove` keywords an event may not necessarily be implemented as a multi-cast delegate. – HappyNomad Jul 15 '13 at 15:29
  • as with [Bob's](http://stackoverflow.com/a/1645484/611007), the other answers mention that the use of this with event handlers is something that should be taken as unreliable... whether that's right or not, this answer could talk about that, too. – n611x007 Jun 27 '14 at 11:00
18

You can change ordering by detaching all handlers, and then re-attaching in desired order.

public event EventHandler event1;

public void ChangeHandlersOrdering()
{
    if (event1 != null)
    {
        List<EventHandler> invocationList = event1.GetInvocationList()
                                                  .OfType<EventHandler>()
                                                  .ToList();

        foreach (var handler in invocationList)
        {
            event1 -= handler;
        }

        //Change ordering now, for example in reverese order as follows
        for (int i = invocationList.Count - 1; i >= 0; i--)
        {
            event1 += invocationList[i];
        }
    }
}
Naser Asadi
  • 1,153
  • 18
  • 35
10

The order is arbitrary. You cannot rely on the handlers being executed in any particular order from one invocation to the next.

Edit: And also - unless this is just out of curiosity - the fact that you need to know is indicative of a serious design problem.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 3
    The order depends on the particular event's implementation, but it is **not** arbitrary. Unless the event's documentation indicates the invocation order, though, I agree it is risky to depend on it. In that vein, I posted a [followup question](http://stackoverflow.com/questions/17659253/documenting-an-events-invocation-order). – HappyNomad Jul 15 '13 at 16:57
  • 10
    To have an event that needs to be handled by different classes in a partircular order does not seem a serious design problem to me. The problem will happen if the event registrations are done in a way that makes difficult to know the order or event to know that the order is important. – Ignacio Soler Garcia Jul 22 '15 at 11:58
8

They are run in the order in which they are registered. RetrieveDataCompleted is a Multicast Delegates. I am looking through reflector to try and verify, and it looks like an array is used behind the scenes to keep track of everything.

Bob
  • 97,670
  • 29
  • 122
  • 130
  • the other answers note that with event handlers this is 'accidental', 'fragile', 'implementation detail', etc., ie. not required by any standard nor convention, it just happens. is that right? in any case, this answer could refer to that too. – n611x007 Jun 27 '14 at 10:57
3

If someone need to do this in the context of a System.Windows.Forms.Form, here is an example inverting the order of Shown event.

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace ConsoleApplication {
    class Program {
        static void Main() {
            Form form;

            form = createForm();
            form.ShowDialog();

            form = createForm();
            invertShownOrder(form);
            form.ShowDialog();
        }

        static Form createForm() {
            var form = new Form();
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown1"); };
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown2"); };
            return form;
        }

        static void invertShownOrder(Form form) {
            var events = typeof(Form)
                .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(form, null) as EventHandlerList;

            var shownEventKey = typeof(Form)
                .GetField("EVENT_SHOWN", BindingFlags.NonPublic | BindingFlags.Static)
                .GetValue(form);

            var shownEventHandler = events[shownEventKey] as EventHandler;

            if (shownEventHandler != null) {
                var invocationList = shownEventHandler
                    .GetInvocationList()
                    .OfType<EventHandler>()
                    .ToList();

                foreach (var handler in invocationList) {
                    events.RemoveHandler(shownEventKey, handler);
                }

                for (int i = invocationList.Count - 1; i >= 0; i--) {
                    events.AddHandler(shownEventKey, invocationList[i]);
                }
            }
        }
    }
}
2

A MulticastDelegate has a linked list of delegates, called an invocation list, consisting of one or more elements. When a multicast delegate is invoked, the delegates in the invocation list are called synchronously in the order in which they appear. If an error occurs during execution of the list then an exception is thrown.

Rahul
  • 21
  • 1
2

During an invocation, methods are invoked in the order in which they appear in the invocation list.

But nobody says that invocation list maintain delegates in the same order as they are added. Thus invocation order is not guaranteed.

ruslanu
  • 21
  • 1
2

This is a function that will place the new event handler function wherever you want in the multidelegate invocation list.

    private void addDelegateAt(ref YourDelegate initial, YourDelegate newHandler, int position)
    {
        Delegate[] subscribers = initial.GetInvocationList();
        Delegate[] newSubscriptions = new Delegate[subscribers.Length + 1];

        for (int i = 0; i < newSubscriptions.Length; i++)
        {
            if (i < position)
                newSubscriptions[i] = subscribers[i];
            else if (i==position)
                newSubscriptions[i] = (YourDelegate)newHandler;
            else if (i > position)
                newSubscriptions[i] = subscribers[i-1];
        }

        initial = (YourDelegate)Delegate.Combine(newSubscriptions);
    }

Then you can always remove the function with a '-=' wherever in your code convenient.

PS - I am not doing any error handling for the 'position' parameter.

chara
  • 73
  • 5
0

I had a similar problem. In my case it was fixed very easily. I'd never seen a delegate that didn't use the += operator. My problem was fixed by having one delegate always added at the end, all the others are always added at the beginning. The OP's example would be something like:

    _webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
    _webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;

In the first case ProcessData1 will be called last. In the 2nd case ProcessData2 will be called first.

Bill
  • 19
  • 2
0

A minimalistic example to show that the order can differ from the order it was added.

public class TestClass
{
    Delegate handlers;

    public event EventHandler Event
    {
        add { handlers = Delegate.Combine(value, handlers ); } // <-- note the order
        remove { handlers = Delegate.Remove(handlers, value); }
    }

    public void RaiseEvents()
    {
        handlers?.DynamicInvoke(this, EventArgs.Empty);
    }
}
Wouter
  • 2,540
  • 19
  • 31
0

One way to manage the order of execution of event handlers in a sequential way is to register only the first handler to the event and invoke the second handler within the first handler routine and in the second handler routine invoke the third one and so on.

Here is a sample code:

public partial class Form1 : Form
{
    EventHandler handler1;
    EventHandler handler2; 
    public Form1()
    {
        InitializeComponent();

        handler1 = ProcessData1;
        handler2 = ProcessData2;
        
        button1.Click += handler1;
    }

    private void ProcessData1(object sender, EventArgs e)
    {
        MessageBox.Show("In handler 1");
        handler2.Invoke(sender, e);
    }

    private void ProcessData2(object sender, EventArgs e)
    {
        MessageBox.Show("In handler 2");
    }
}
0

The other answers have established that it's very difficult to guarantee execution order. It's probably true that the events are executed in the order registered, but it might be difficult in your program to guarantee that order. For example, I have an application where events are registered by static class constructors, and apparently C# doesn't call those constructors until the class gets utilized the first time, and who knows what order that will be in.

So, many of the solutions proposed are somewhat complicated methods of changing the order, including Naser Asadi's interesting one of re-registering events. After much thought, I came up with a much simpler approach. I decided to NOT ALTER the registration order, but rather alter what happens in each event handler.

In my application, what I'm trying to do is customize a string, but multiple event handlers might customize it differently, and I don't want to rely on potluck. Instead, each handler has a different "PRIORITY". Each time the string is updated, the priority is updated also. Other handlers can't touch it unless they have a higher priority. It's a brilliantly simple solution; you merely need a way to establish a priority for each handler.

Below is an example of one of the handlers. Note that I could create a similar class and handler for vanilla ice cream also, with a different priority, and if both chocolate and vanilla are registered for the same ice cream shop, the one with higher priority will be the one that decides what the ice cream flavor is. Note that it DOES NOT MATTER if vanilla or chocolate registers their handler first! Only the priority matters, which YOU can control.

public delegate void CustomizeStringEventHandler(Object sender, CustomizeStringEventArgs e);

public class CustomizeStringEventArgs : EventArgs {
  string _description;
  int _priority;
}

//-----------------------

public class ExampleChocolateIceCreamFlavorClass {
  
  static ExampleChocolateIceCreamFlavorClass() {
    DowntownIceCreamShop.LookupDescription += new CustomizeStringEventHandler(CustomizeString);
    CountryIceCreamShop.LookupDescription += new CustomizeStringEventHandler(CustomizeString);
  }
  
  static void CustomizeString(object sender, CustomizeStringEventArgs e) {
    string desc = "Chocolate"
    int priority = 50; //priority for chocolate
    if ((desc != null) && ((priority > e.Priority) || (e.Description == null))) {
      e.Description = desc;
      e.Priority = priority;
    }
  }
  
}
Kevin North
  • 194
  • 1
  • 6