【问题标题】:Order of event handler execution事件处理程序执行顺序
【发布时间】:2009-10-29 17:54:51
【问题描述】:

如果我设置多个事件处理程序,像这样:

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

当事件RetrieveDataCompleted 被触发时,处理程序的运行顺序是什么?它们是否在同一个线程中按注册顺序依次运行?

【问题讨论】:

  • 答案将特定于 RetrieveDataCompleted 事件。如果它具有多播委托的默认后备存储,那么是的,“它们在同一个线程中运行,并按注册的顺序依次运行”。

标签: c# asynchronous event-handling


【解决方案1】:

目前,它们按照注册的顺序执行。但是,这是一个实现细节,我不会依赖这种行为在未来的版本中保持不变,因为规范没有要求。

【讨论】:

  • 我想知道,为什么投反对票?这完全正确,直接回答了这个问题……
  • @Rawling:这是用于二元运算符重载解决方案 - 不是事件处理。在这种情况下,这不是加法运算符。
  • 啊,我知道我哪里错了:“事件处理程序是代表,对吧?”。我现在知道他们不是。给自己写了一个以相反顺序触发处理程序的事件,只是为了向自己证明:)
  • 澄清一下,顺序取决于特定事件的后备存储。事件的默认后备存储,多播委托,被记录为按注册顺序执行。这在未来的框架版本中不会改变。可能会改变的是用于特定事件的后备存储。
  • 投反对票,因为它在 2 点上实际上是不正确的。 1) 目前它们按照 特定事件 的实现顺序执行 - 因为您可以为事件实现自己的添加/删除方法。 2)当通过多播委托使用默认事件实现时,顺序实际上是规范要求的。
【解决方案2】:

委托的调用列表是 一组有序的委托,其中 列表的每个元素调用 正是由调用的方法之一 代表。调用列表可以 包含重复的方法。期间 调用,委托调用方法 按照它们出现的顺序 调用列表

从这里: Delegate Class

【讨论】:

  • 很好,但是使用addremove 关键字,事件不一定被实现为多播委托。
  • Bob's 一样,其他答案提到将 this 与事件处理程序一起使用是不可靠的......无论这是否正确,这个答案都可以谈论,也是。
【解决方案3】:

您可以通过分离所有处理程序来更改顺序,然后按所需顺序重新附加。

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

【讨论】:

    【解决方案4】:

    顺序是任意的。您不能依赖从一个调用到下一个调用以任何特定顺序执行的处理程序。

    编辑:而且 - 除非只是出于好奇 - 您需要知道的事实表明存在严重设计问题。

    【讨论】:

    • 顺序取决于特定事件的实现,但它不是任意的。但是,除非事件的文档表明调用顺序,否则我同意依赖它是有风险的。在这种情况下,我发布了followup question
    • 有一个事件需要由不同的类以特定的顺序处理,这对我来说似乎不是一个严重的设计问题。如果事件注册以难以知道顺序或事件以知道顺序很重要的方式完成。
    【解决方案5】:

    它们按照注册的顺序运行。 RetrieveDataCompletedMulticast Delegates。我正在通过反射器尝试验证,看起来像在幕后使用了一个数组来跟踪所有内容。

    【讨论】:

    • 其他答案请注意,对于事件处理程序,这是“意外”、“脆弱”、“实施细节”等,即。任何标准或惯例都没有要求,它只是发生了。是对的吗?无论如何,这个答案也可以参考。
    【解决方案6】:

    如果有人需要在 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]);
                    }
                }
            }
        }
    }
    

    【讨论】:

      【解决方案7】:

      MulticastDelegate 有一个委托的链接列表,称为调用列表,由一个或多个元素组成。当调用多播委托时,调用列表中的委托会按照它们出现的顺序被同步调用。如果在执行列表期间发生错误,则会引发异常。

      【讨论】:

        【解决方案8】:

        在调用期间,方法按照它们在调用列表中出现的顺序被调用。

        但是没有人说调用列表维护委托的顺序与添加它们的顺序相同。 因此不能保证调用顺序。

        【讨论】:

          【解决方案9】:

          这是一个函数,它将新的事件处理函数放置在多委托调用列表中的任何位置。

              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 - 我没有对“位置”参数进行任何错误处理。

          【讨论】:

            【解决方案10】:

            我遇到了类似的问题。就我而言,它很容易修复。我从未见过不使用 += 运算符的代表。我的问题是通过在最后添加一个代表来解决的,所有其他的总是在开头添加。 OP 的示例类似于:

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

            在第一种情况下,ProcessData1 将被最后调用。在第二种情况下,将首先调用 ProcessData2。

            【讨论】:

              【解决方案11】:

              一个简单的例子,表明订单可以不同于添加的订单。

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

              【讨论】:

                猜你喜欢
                • 2011-10-27
                • 1970-01-01
                • 2012-02-20
                • 2011-03-28
                • 2020-11-29
                • 2012-05-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多