次のように複数のイベントハンドラーを設定した場合:
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
イベントRetrieveDataCompleted
が発生したときにハンドラーはどの順序で実行されますか?それらは同じスレッドで実行され、登録された順序で順番に実行されますか?
次のように複数のイベントハンドラーを設定した場合:
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
イベントRetrieveDataCompleted
が発生したときにハンドラーはどの順序で実行されますか?それらは同じスレッドで実行され、登録された順序で順番に実行されますか?
回答:
現在、登録された順に実行されます。ただし、これは実装の詳細であり、仕様で要求されていないため、将来のバージョンでも同じ動作を維持することには依存しません。
デリゲートの呼び出しリストは、リストの各要素がデリゲートによって呼び出されたメソッドの1つを呼び出す順序付けられたデリゲートのセットです。呼び出しリストには、重複するメソッドを含めることができます。呼び出し中、デリゲートは、呼び出しリストに表示されている順序でメソッドを呼び出します。
ここから: デリゲートクラス
add
and remove
キーワードを使用すると、イベントは必ずしもマルチキャストデリゲートとして実装されない場合があります。
すべてのハンドラーを切り離してから、希望の順序で再接続することにより、順序を変更できます。
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];
}
}
}
順序は任意です。ある呼び出しから次の呼び出しまで、特定の順序で実行されるハンドラーに依存することはできません。
編集:そしてまた-これが単に好奇心から外れていない限り-あなたが知る必要があるという事実は、深刻な設計上の問題を示しています。
登録された順に実行されます。RetrieveDataCompleted
はマルチキャストデリゲートです。私はリフレクターを試して確認していますが、すべてを追跡するために舞台裏でアレイが使用されているようです。
誰かが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]);
}
}
}
}
}
これは、マルチデリゲート呼び出しリストのどこにでも新しいイベントハンドラー関数を配置する関数です。
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);
}
その後、コード内の都合の良い場所ならどこでも、「-=」を使用して関数をいつでも削除できます。
PS-'position'パラメータのエラー処理は行っていません。
同様の問題がありました。私の場合、それは非常に簡単に修正されました。+ =演算子を使用しないデリゲートを見たことはありません。私の問題は、1つのデリゲートが常に最後に追加され、他のすべてが常に最初に追加されることで修正されました。OPの例は次のようになります。
_webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
_webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;
最初のケースでは、ProcessData1が最後に呼び出されます。2番目のケースでは、ProcessData2が最初に呼び出されます。