我是不是使用正确的方法来监视创建句柄时要执行的任务?

Posted

技术标签:

【中文标题】我是不是使用正确的方法来监视创建句柄时要执行的任务?【英文标题】:Am I using the right approach to monitor the tasks I want to perform when a handle is created?我是否使用正确的方法来监视创建句柄时要执行的任务? 【发布时间】:2010-12-06 00:08:37 【问题描述】:

是否有一个普遍接受的最佳实践来创建一个取消订阅的事件处理程序?

例如,我想出的第一件事是:

// Foo.cs

// ...
Bar bar = new Bar(/* add'l req'd state */);
EventHandler handler = new EventHandler(bar.HandlerMethod);

bar.HandlerToUnsubscribe = handler;

eventSource.EventName += handler;
// ...

// Bar.cs

class Bar

    /* add'l req'd state */

    // .ctor

    public EventHandler HandlerToUnsubscribe  get; set; 

    public void HandlerMethod(object sender, EventArgs args)
    
        // Do what must be done w/ add'l req'd state
        ((EventSourceType)sender).EventName -= this.HandlerToUnsubscribe;
    


要说这感觉很老套/不好,这是轻描淡写的。它与时间依赖性紧密耦合(HandlerToUnsubscribe 必须在正确的时间被分配正确的值)。我觉得在这种情况下我一定是在扮演一个复杂的角色——我错过了什么愚蠢或简单的东西吗?

上下文:

我正在创建 UI 和 Winforms 中的专有命令基础结构之间的绑定(使用 System.Windows.Input 中有用的ICommand)。绑定基础结构的一个方面是,在 UI 命令组件(如工具栏按钮或菜单项)之间创建绑定的用户可以选择监听命令的 CanExecuteChanged 事件,然后根据该事件更新 UI 的状态——通常将 Enabled 属性设置为 truefalse

该技术通常工作得很好,但是有一些方法可以在创建 ui 组件的句柄之前触发事件。我试图保证提供的处理程序不会运行,除非已创建句柄。结果,我正在考虑提供一个通用的帮助类(“Bar”)来帮助实现。 Bar 的目标是检查是否存在适当的句柄。如果是这样,太好了!如果没有,它将订阅适当的IsHandleCreated 事件,以便在最终创建句柄时运行提供的处理程序。 (这很重要,因为客户端可以在存在句柄之前在 UI 的 .ctor 中设置它们的绑定。)但是,我希望这个订阅是完全透明的,所以我还希望每个事件处理程序自动取消订阅 @ 987654331@ 运行完成后。

我仍然在试图弄清楚这是否是一个好主意,所以我还没有概括这个概念——在这种情况下,我只是直接针对 ToolStripItems 实现它来验证这个想法是合理的。不过,我还没有卖掉它。

我知道我还可以选择仅在表单的 OnLoad 事件(例如)中强制创建 UI 句柄后才能创建绑定。我知道这行得通,我过去做过。不过,我想看看在这种情况下我是否可以缓解该特定要求。如果它甚至实用。

【问题讨论】:

【参考方案1】:

只运行一次的处理程序怎么样? 像这样的:

如果(是运行) 返回; 是运行=真;

【讨论】:

【参考方案2】:

我通常会执行以下操作来实现 1 次事件处理程序。

void OnControlClickOneTime(this Control c, EventHandler e) 
  EventHandler e2 = null;
  e2 = (s,args) => 
    c.Click -= e2;
    e(s,args);
  ;
  c.Click += e2;

【讨论】:

【参考方案3】:

格雷格,

你所拥有的不是观察者模式,而是一个消息队列。所以你只是为你试图解决的问题使用了错误的设计模式。

使用QueueActionobject 从头开始​​实现您自己的消息队列很容易,其中对象自己排队,您只需在调用它们时将项目出队。

【讨论】:

哦!聪明的!这正是我认为我必须缺少的东西:明显和愚蠢。谢谢你打倒我的内部并发症,朱丽叶! :D【参考方案4】:

您可以在此处使用 run once 方法,这里已经提到过几次,但根据您的使用情况,会有一些问题。

1) 您可能希望稍后再次重新挂钩该方法,并让它再次运行。虽然我想你可以重置你的布尔值

2) 您仍然拥有该引用,最终可能会将您的类保留在内存中,而不是被垃圾收集。

一种选择是在定义事件处理时使用匿名方法和闭包:

public class Foo

    public EventHandler<EventArgs> MyEvent;

    public void FireEvent()
    
        if(MyEvent != null)
            MyEvent(this, EventArgs.Empty);
    


Foo obj = new Foo();

Action<object, EventArgs> action = new Action<object, EventArgs>((sender, args) =>

    // We're now running the event handler, so unsubscribe
    obj.MyEvent -= new EventHandler<EventArgs>(action);

    // Do whatever you wanted to do when the event fired.
);

obj.MyEvent += new EventHandler<EventArgs>(action);

【讨论】:

【参考方案5】:

在 CodeProject 上有一篇详尽的文章:Weak events,其中介绍了该问题的几种解决方案。

【讨论】:

【参考方案6】:

您可以使用 Wea​​kReference 对象来介绍弱订阅者。在事件触发期间,您可以检查是否已经收集了弱引用,并在必要时从订阅者列表中删除此订阅者。

这背后的基本思想是,当订阅者被 GC 收集时,您的处理程序会注意到它并将它们从订阅者列表中丢弃。

【讨论】:

【参考方案7】:

通常的方法是存储一个布尔值来判断它是否应该运行......

bool runMyEvent = true;

void Handler(object sender, EventArgs e) 
   if (runMyEvent) 
      // handler here
      runMyEvent = false;
    else 
      return;
   

【讨论】:

【参考方案8】:

如果有意义(并且如果您可以访问调用处理程序的代码),也许您可​​以在运行 handler 后删除所有事件。

我也不知道这是否是个好主意,只是另一个想法。

【讨论】:

以上是关于我是不是使用正确的方法来监视创建句柄时要执行的任务?的主要内容,如果未能解决你的问题,请参考以下文章

RTOS中的任务句柄到底是什么意思?

CoreData + Restkit 当用户退出应用程序时要执行啥任务?

如何从显示设备名称中获取 HMONITOR 句柄?

在调试期间在SSIS中监视变量

Win32:捕获句柄到显示监视器

监视 nodemon js 或 npm 中的新文件创建事件