【问题标题】:Event handling raising InvalidOperationException - looking for advice引发 InvalidOperationException 的事件处理 - 寻求建议
【发布时间】:2014-05-08 07:49:55
【问题描述】:

我对以这种方式编程有点陌生 - 有没有一种我可以解决的方法或使用事件和处理程序的推荐做法?

例如:

class objectA
{    
    public List<Handler> handlers;
    ...
    public onActionHappened
    {
        foreach(Handler h in handlers)
        {
            raiseEvent(this, eventArgs);
        }
    }

    ...
    public void DeleteThis()
    {
        handlers = null
    }
}

raiseEvent() 将继续调用其他一些方法,其中之一将调用 DeleteThis()。当一切结束,程序流程返回foreach循环的“}”处的raiseEvent()时,发现handler已被修改=null,从而抛出InvalidOperationException的错误。

某些方法处理应该禁用此 objectA 作为功能的一部分 - 因此 Deletethis() 可能会在某些时候被客户端代码调用。为了解决这个问题,我已经从 List 处理程序修改为只有一个 Handler 对象,但我觉得这应该是一种更好的解决方法。或者更好的编码方式。

有什么建议吗?提前致谢!

【问题讨论】:

    标签: c# event-handling invalidoperationexception


    【解决方案1】:

    如果您在列表中使用ToArray,您将创建其内容的副本并且不依赖于处理程序变量本身:

       foreach(Handler h in handlers.ToArray()
            {
                //optional break if you don't want the loop to continue after DeleteThis is called:  if(handlers==null)break;
                raiseEvent(this, eventArgs);
            }
    

    【讨论】:

      【解决方案2】:

      解决您问题的核心:解决问题的最直接方法是在枚举列表之前将列表分配给局部变量。

      class objectA
      {    
          public List<Handler> handlers;
          ...
          public void OnActionHappened()
          {
              List<Handler> lh = handlers;
      
              // TODO: Would probably make sense to check if lh is null here.
      
              foreach(Handler h in lh)
              {
                  h.raiseEvent(this, eventArgs);
              }
          }
          ...
          public void DeleteThis()
          {
              handlers = null;
          }
      }
      

      确实没有必要像其他地方建议的那样创建列表的副本。

      由于您似乎是 C# 编程的新手,让我告诉您这里发生了什么。

      List&lt;T&gt; 是一个引用类型。让我们假设您通过调用其构造函数创建了一个新的List&lt;T&gt;

      List<Handler> handlers = new List<Handler>();
      

      现在,执行此语句会在计算机内存中创建两件事:

      1. 列表对象本身。
      2. 引用列表对象的变量(“处理程序”)。

      现在,如果计算机执行以下行:

      List<Handler> lh = handlers;
      

      我们最终会得到这样的结果:

      最后,如果计算机执行以下行:

      handlers = null;
      

      情况如下:

      如您所见,这样我们通过本地列表变量“lh”维护对列表对象的有效引用,并将成员变量“handlers”设置为 null 不再影响 foreach 枚举。

      【讨论】:

        【解决方案3】:

        不能在定义事件的类之外触发事件。因此,如果将处理程序移到 A 类之外,则不能再在 A 类的处理程序中触发事件。

        要解决此问题,请将处理程序放在另一个类中,例如 B 类,并定义一个公共方法来触发 B 类中的处理程序中的事件(在本例中为 onActionHappened 方法)。对于 A 类,只需调用 B 类的公共方法 (onActionHappened)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-01-12
          • 2018-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-18
          • 2012-04-22
          • 2016-04-29
          相关资源
          最近更新 更多