我对这种方式的编程有点新意 - 有什么方法可以解决或建议使用事件和处理程序?
例如:
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()时,它会发现处理程序已经被修改= null,因此抛出了InvalidOperationException的错误。
某些方法处理应该禁用此objectA作为功能的一部分 - 因此可能在某些时候由客户端代码调用Deletethis()。为了解决这个问题,我已经从List处理程序修改为只有一个Handler对象,但我觉得这应该是一种更好的解决方法。或者更好的编码方式。
有什么建议吗?提前谢谢!
答案 0 :(得分: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);
}
答案 1 :(得分:0)
无法在定义事件的类之外触发事件。因此,如果您在A类之外移动处理程序,则不能再在A类中的处理程序中触发事件。
要解决此问题,请将处理程序放在另一个类中,比如说B类,并定义一个公共方法,该方法触发B类处理程序中的事件(在本例中为onActionHappened方法)。对于A类,只需调用B类的公共方法(onActionHappened)。
答案 2 :(得分:0)
解决问题的核心:解决问题的最直接方法是在枚举之前将列表分配给局部变量。
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<T>
是参考类型。我们假设你通过调用它的构造函数来创建一个新的List<T>
:
List<Handler> handlers = new List<Handler>();
现在,执行此语句会在计算机的内存中创建两件事:
现在,如果计算机执行以下行:
List<Handler> lh = handlers;
我们最终会得到这样的结果:
最后,如果计算机执行以下行:
handlers = null;
情况如下:
正如您所看到的,这样我们通过本地列表变量“lh”维护对列表对象的有效引用,并将成员变量“handlers”设置为null不会再影响foreach枚举。