是否已添加事件处理程序?

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 时这不会抛出吗? 在拥有事件处理程序的类之外,您只能使用 -= 和 +=。您无法访问该活动。

以上是关于是否已添加事件处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

js添加事件处理程序

是否可以向 ZeroMQ 添加事件处理以在接收/发送数据时采取行动?

jQuery.on() 是不是适用于在创建事件处理程序后添加的元素?

前端面试之js篇

深入理解js Dom事件机制——添加事件处理程序

jQuery on() 方法 为选定已存在元素和未来元素绑定标准事件和自定义事件