Order of event handler execution

C#AsynchronousEvent Handling

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

> 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

Solution 3 - C#

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];
        }
    }
}

Solution 4 - C#

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.

Solution 5 - C#

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.

Solution 6 - C#

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]);
                }
            }
        }
    }
}

Solution 7 - C#

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.

Solution 8 - C#

> 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.

Solution 9 - C#

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.

Solution 10 - C#

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.

Solution 11 - C#

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);
    }
}

Solution 12 - C#

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");
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhillip NganView Question on Stackoverflow
Solution 1 - C#Reed CopseyView Answer on Stackoverflow
Solution 2 - C#Philip WallaceView Answer on Stackoverflow
Solution 3 - C#Naser AsadiView Answer on Stackoverflow
Solution 4 - C#Rex MView Answer on Stackoverflow
Solution 5 - C#BobView Answer on Stackoverflow
Solution 6 - C#Fábio Augusto PandolfoView Answer on Stackoverflow
Solution 7 - C#RahulView Answer on Stackoverflow
Solution 8 - C#ruslanuView Answer on Stackoverflow
Solution 9 - C#charaView Answer on Stackoverflow
Solution 10 - C#BillView Answer on Stackoverflow
Solution 11 - C#WouterView Answer on Stackoverflow
Solution 12 - C#Amirhossein NayebiastanehView Answer on Stackoverflow