是否已添加事件处理程序?
Posted
技术标签:
【中文标题】是否已添加事件处理程序?【英文标题】:Has an event handler already been added? 【发布时间】:2010-09-13 07:29:40 【问题描述】:有没有办法判断事件处理程序是否已添加到对象中?我正在序列化一个对象列表进入/退出会话状态,这样我们就可以使用基于 SQL 的会话状态...当列表中的对象具有更改的属性时,它需要被标记,事件处理程序之前已妥善处理.但是现在当对象被反序列化时,它没有得到事件处理程序。
出于轻微的烦恼,我只是将事件处理程序添加到访问对象的 Get 属性中。它现在被调用了,这很棒,除了它被调用了 5 次,所以我认为每次访问对象时都会不断添加处理程序。
忽略它确实足够安全,但我宁愿通过检查是否已添加处理程序来使其更清晰,所以我只这样做一次。
这可能吗?
编辑:我不一定完全控制添加的事件处理程序,因此仅检查 null 是不够的。
【问题讨论】:
另见***.com/questions/367523/… 【参考方案1】:我最近遇到了类似的情况,我只需要为一个事件注册一次处理程序。我发现你可以安全地先注销,然后再注册,即使处理程序根本没有注册:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
请注意,每次注册处理程序时都这样做将确保您的处理程序只注册一次。 听起来对我来说是一个很好的做法:)
【讨论】:
似乎有风险;如果在删除处理程序之后并在重新添加之前触发事件,它将被错过。 当然。你的意思是它不是线程安全的。但这只会在运行多个线程等时出现问题,这并不常见。在大多数情况下,为了简单起见,这应该足够了。 这让我很困扰。仅仅因为您当前没有在代码中显式创建线程,这并不意味着没有多个线程,或者以后不会添加它们。只要您(或团队中的其他人,可能几个月后)添加工作线程或同时响应 UI 和网络连接,这就会为高度间歇性丢弃事件打开大门。 我相信删除和再次添加之间的时间窗口非常小,不太可能存在错过的事件,但是是的,它仍然有可能。 如果您将其用于更新 UI 等,那么这是一项微不足道的任务,风险是可以的。如果这是用于网络数据包处理等,那么我不会使用它。【参考方案2】:正如@Telos 所提到的,从定义类之外,您只能在+=
或-=
的左侧使用EventHandler。因此,如果您有能力修改定义类,您可以通过检查事件处理程序是否为null
来提供执行检查的方法 - 如果是,则没有添加任何事件处理程序。如果没有,那么也许你可以遍历中的值
Delegate.GetInvocationList。如果一个等于您要添加为事件处理程序的委托,那么您就知道它在那里。
public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
if ( this.EventHandler != null )
foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
if ( existingHandler == prospectiveHandler )
return true;
return false;
这可以很容易地修改为“添加处理程序,如果它不存在”。如果您无法访问暴露事件的课程的内部信息,您可能需要按照@Lou Franco 的建议探索-=
和+=
。
但是,您最好重新检查一下您调试和停用这些对象的方式,看看您是否找不到自己跟踪这些信息的方法。
【讨论】:
这个不能编译,EventHandler只能在+=或者-=的左边。 在进一步解释后取消投票。 SQL 状态几乎破坏了这里的整个想法...... :( 感谢 Blair 和 SO search,正是我正在寻找的东西(虽然很烦你不能在课堂外这样做) 在比较代表是否相等时,大部分时间都会出现问题。因此,如果您想检查完全相同的委托存在,请使用Delegate.Equals(objA, objB)
。否则单独比较属性,如if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName)
。
此代码在 WinForm 中不起作用。是否严格用于 ASP.NET?【参考方案3】:
如果这是唯一的处理程序,您可以检查事件是否为空,如果不是,则处理程序已添加。
我认为即使未添加事件,您也可以使用处理程序安全地调用 -= (如果没有,您可以捕获它)——以确保在添加之前它不存在。
【讨论】:
一旦事件在其他地方被处理,这个逻辑就会中断。【参考方案4】:这个例子展示了如何使用 GetInvocationList() 方法来检索所有已添加的处理程序的委托。如果您要查看是否已添加特定的处理程序(函数),则可以使用数组。
public class MyClass
event Action MyEvent;
...
MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;
...
Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example
Console.WriteLine(handlers[0].Method.Name);//prints the name of the method
您可以检查委托的 Method 属性上的各种属性,以查看是否已添加特定功能。
如果您要查看是否只有一个附加,您可以测试 null。
【讨论】:
GetInvocationList() 不是我班的成员。事实上,我似乎无法在我有权访问的任何对象或处理程序上找到此方法... 我也试过了,显然你只能从课堂内部访问这样的事件。我一般都是这样做的,正如其他人提到的那样,事件处理程序可能无论如何都会迷路。感谢您的澄清!【参考方案5】:如果我正确理解您的问题,您可能会遇到更大的问题。您说其他对象可能订阅这些事件。当对象被序列化和反序列化时,其他对象(您无法控制的对象)将失去它们的事件处理程序。
如果您不担心这一点,那么保留对事件处理程序的引用就足够了。如果您担心其他对象丢失其事件处理程序的副作用,那么您可能需要重新考虑您的缓存策略。
【讨论】:
天啊!甚至没有想到...虽然考虑到我最初的问题是我自己的处理程序迷路了,这应该很明显。【参考方案6】:对我有用的唯一方法是创建一个布尔变量,我在添加事件时将其设置为 true。然后我问:如果变量为假,我添加事件。
bool alreadyAdded = false;
这个变量可以是全局的。
if(!alreadyAdded)
myClass.MyEvent += MyHandler;
alreadyAdded = true;
【讨论】:
【参考方案7】:我同意 alf 的回答,但对它的修改很少,, 使用,
try
control_name.Click -= event_Click;
main_browser.Document.Click += Document_Click;
catch(Exception exce)
main_browser.Document.Click += Document_Click;
【讨论】:
【参考方案8】:EventHandler.GetInvocationList().Length > 0
【讨论】:
当列表 == null 时这不会抛出吗? 在拥有事件处理程序的类之外,您只能使用 -= 和 +=。您无法访问该活动。以上是关于是否已添加事件处理程序?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以向 ZeroMQ 添加事件处理以在接收/发送数据时采取行动?