web-dev-qa-db-ja.com

イベントハンドラーの実行順序

次のように複数のイベントハンドラーを設定した場合:

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

イベントRetrieveDataCompletedが発生したときにハンドラーはどの順序で実行されますか?それらは同じスレッドで実行され、登録された順番で順番に実行されますか?

82
Phillip Ngan

現在、それらは登録された順に実行されます。ただし、これは実装の詳細であり、仕様で要求されていないため、将来のバージョンでこの動作が変わらないことに依存しません。

118
Reed Copsey

デリゲートの呼び出しリストは、リストの各要素がデリゲートによって呼び出されたメソッドの1つを呼び出すデリゲートの順序付きセットです。呼び出しリストには、重複したメソッドを含めることができます。呼び出し中、デリゲートは、呼び出しリストに表示される順序でメソッドを呼び出します

ここから: デリゲートクラス

50
Philip Wallace

順序を変更するには、すべてのハンドラーをデタッチしてから、必要な順序で再アタッチします。

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];
        }
    }
}
9
Naser Asadi

それらは登録された順に実行されます。 RetrieveDataCompletedマルチキャストデリゲート です。私はリフレクターを覗いて検証しようとしていますが、すべてを追跡するために背後で配列が使用されているようです。

8
Bob

順序は任意です。ある呼び出しから次の呼び出しまで、特定の順序で実行されるハンドラーに依存することはできません。

編集:そして-これが好奇心だけではない場合-あなたが知る必要があるという事実は、深刻なを示しています設計上の問題。

8
Rex M

MulticastDelegateには、呼び出しリストと呼ばれる、1つ以上の要素で構成されるデリゲートのリンクリストがあります。マルチキャストデリゲートが呼び出されると、呼び出しリスト内のデリゲートは、表示される順序で同期的に呼び出されます。リストの実行中にエラーが発生すると、例外がスローされます。

2
Rahul

System.Windows.Forms.Formのコンテキストでこれを行う必要がある場合は、Shownイベントの順序を逆にする例を次に示します。

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

呼び出し中、メソッドは呼び出しリストに表示される順序で呼び出されます。

しかし、呼び出しリストが追加されたのと同じ順序でデリゲートを維持すると言う人はいません。したがって、呼び出し順序は保証されません。

1
ruslanu