如果我设置了多个事件处理程序,如下所示:
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
事件RetrieveDataCompleted
触发时处理程序的运行顺序是什么?它们是否在相同的线程中运行,并按注册的顺序顺序运行?
Answers:
当前,它们按照注册的顺序执行。但是,这是一个实现细节,我不会依赖此行为在将来的版本中保持不变,因为规范没有要求。
委托的调用列表是一组有序的委托,其中列表的每个元素都精确地调用委托调用的方法之一。调用列表可以包含重复的方法。在调用过程中,委托以它们在调用列表中出现的顺序调用方法。
从这里: 代表班
add
和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'参数进行任何错误处理。
我有一个类似的问题。就我而言,它很容易修复。我从未见过没有使用+ =运算符的委托。我的问题已得到解决,方法是始终在末尾添加一个代表,而所有其他代表总是在开头添加。OP的示例如下所示:
_webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
_webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;
在第一种情况下,ProcessData1将被最后调用。在第二种情况下,将首先调用ProcessData2。