Ali*_*fai 93 c# delegates event-handling
重复:如何确保事件仅订阅一次 并且已添加事件处理程序?
我有一个提供一些服务的单例,我的类挂钩到它上面的一些事件,有时一个类挂起两次事件然后被调用两次.我正在寻找一种经典的方法来防止这种情况发生.不知怎的,我需要检查一下我是否已经迷上了这个事件......
Pri*_*TSS 165
-=如果没有找到,则首先删除事件如何不抛出异常
/// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);
Run Code Online (Sandbox Code Playgroud)
Jud*_*ngo 144
显式实现事件并检查调用列表.您还需要检查null:
using System.Linq; // Required for the .Contains call below:
...
private EventHandler foo;
public event EventHandler Foo
{
add
{
if (foo == null || !foo.GetInvocationList().Contains(value))
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Run Code Online (Sandbox Code Playgroud)
使用上面的代码,如果调用者多次订阅该事件,它将被简单地忽略.
Lox*_*Lox 34
我测试了每个解决方案,最好的解决方案(考虑性能)是:
private EventHandler _foo;
public event EventHandler Foo {
add {
_foo -= value;
_foo += value;
}
remove {
_foo -= value;
}
}
Run Code Online (Sandbox Code Playgroud)
没有Linq使用必需.在取消订阅之前无需检查null(有关详细信息,请参阅MS EventHandler).无需记住在任何地方取消订阅.
JP *_*oto 19
你真的应该在接收器级而不是源级别处理这个问题.也就是说,不要在事件源处规定事件处理程序逻辑 - 将其留给处理程序(接收器)本身.
作为服务的开发者,你是说谁只能注册一次?如果他们出于某种原因想要注册两次怎么办?如果你试图通过修改源来纠正接收器中的错误,那么这也是在接收器级纠正这些问题的一个很好的理由.
我相信你有理由; 重复接收器非法的事件源并不是不可思议的.但也许您应该考虑一种替代架构,使事件的语义保持不变.
ang*_*son 14
您需要在事件上实现添加和删除访问器,然后检查委托的目标列表,或将目标存储在列表中.
在add方法中,您可以使用Delegate.GetInvocationList方法获取已添加到委托的目标列表.
由于委托被定义为比较相等,如果它们链接到同一目标对象上的相同方法,您可能可以运行该列表并进行比较,如果您发现没有比较相等,则添加新的.
这是示例代码,编译为控制台应用程序:
using System;
using System.Linq;
namespace DemoApp
{
public class TestClass
{
private EventHandler _Test;
public event EventHandler Test
{
add
{
if (_Test == null || !_Test.GetInvocationList().Contains(value))
_Test += value;
}
remove
{
_Test -= value;
}
}
public void OnTest()
{
if (_Test != null)
_Test(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
tc.Test += tc_Test;
tc.Test += tc_Test;
tc.OnTest();
Console.In.ReadLine();
}
static void tc_Test(object sender, EventArgs e)
{
Console.Out.WriteLine("tc_Test called");
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
tc_Test called
Run Code Online (Sandbox Code Playgroud)
(即只有一次)
Microsoft的Reactive Extensions(Rx)框架也可用于"仅订阅一次".
给定一个鼠标事件foo.Clicked,这里是如何订阅和只接收一个调用:
Observable.FromEvent<MouseEventArgs>(foo, nameof(foo.Clicked))
.Take(1)
.Subscribe(MyHandler);
...
private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
// This will be called just once!
var sender = eventInfo.Sender;
var args = eventInfo.EventArgs;
}
Run Code Online (Sandbox Code Playgroud)
除了提供"订阅一次"功能外,RX方法还提供了将事件组合在一起或过滤事件的功能.这很漂亮.
| 归档时间: |
|
| 查看次数: |
86346 次 |
| 最近记录: |