阻止并等待事件

Ras*_*ber 35 .net c# events

它有时希望在等待事件发生时阻塞我的线程.

我通常会这样做:

private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

private void OnEvent(object sender, EventArgs e){
  _autoResetEvent.Set();
}

// ...
button.Click += OnEvent;
try{
  _autoResetEvent.WaitOne();
}
finally{
  button.Click -= OnEvent;
}
Run Code Online (Sandbox Code Playgroud)

但是,似乎这应该是我可以提取到一个公共类(或者甚至可能已经存在于框架中的东西)的东西.

我希望能够做到这样的事情:

EventWaiter ew = new EventWaiter(button.Click);
ew.WaitOne();
EventWaiter ew2 = new EventWaiter(form.Closing);
ew2.WaitOne();
Run Code Online (Sandbox Code Playgroud)

但我真的找不到构建这样一个类的方法(我找不到一个好的方法来将事件作为参数传递).有人可以帮忙吗?

举一个为什么这个有用的例子,考虑这样的事情:

var status = ShowStatusForm();
status.ShowInsertUsbStick();
bool cancelled = WaitForUsbStickOrCancel();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
Run Code Online (Sandbox Code Playgroud)

这比使用在许多方法之间展开的逻辑编写的等效代码更简洁和可读.但是要实现WaitForUsbStickOrCancel(),WaitForUsbStickToBeRemoved和WaitUntilUserPressesDone()(假设我们在插入或删除usb棒时得到一个事件)我需要每次都重新实现"EventWaiter".当然,你必须要小心,永远不要在GUI线程上运行它,但有时这对于更简单的代码来说是值得的权衡.

替代方案看起来像这样:

var status = ShowStatusForm();
status.ShowInsertUsbStick();
usbHandler.Inserted += OnInserted;
status.Cancel += OnCancel;
//...
void OnInserted(/*..*/){
  usbHandler.Inserted -= OnInserted;
  status.ShowWritingOnUsbStick();
  MethodInvoker mi = () => WriteOnUsbStick();
  mi.BeginInvoke(WritingDone, null);
}
void WritingDone(/*..*/){
  /* EndInvoke */
  status.AskUserToRemoveUsbStick();
  usbHandler.Removed += OnRemoved;
}
void OnRemoved(/*..*/){
  usbHandler.Removed -= OnRemoved;
  status.ShowFinished();
  status.Done += OnDone;
}
/* etc */
Run Code Online (Sandbox Code Playgroud)

我发现阅读起来更难.不可否认,流量将永远不会那么线性,但是当它如此时,我喜欢第一种风格.

它与使用ShowMessage()和Form.ShowDialog()相当 - 它们也会阻塞直到某些"事件"发生(尽管如果在gui-thread上调用它们将运行消息循环).

Kal*_*dje 4

我修改了 Dead.Rabit 的 EventWaiter 类来处理EventHandler<T>. 因此,您可以使用 等待所有事件类型EventHandler<T>,这意味着您的委托类似于delegate void SomeDelegate(object sender, T EventsArgs).

 public class EventWaiter<T>
{

    private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    private EventInfo _event = null;
    private object _eventContainer = null;

    public EventWaiter(object eventContainer, string eventName)
    {
        _eventContainer = eventContainer;
        _event = eventContainer.GetType().GetEvent(eventName);
    }

    public void WaitForEvent(TimeSpan timeout)
    {
        EventHandler<T> eventHandler = new EventHandler<T>((sender, args) => { _autoResetEvent.Set(); });
        _event.AddEventHandler(_eventContainer, eventHandler);
        _autoResetEvent.WaitOne(timeout);
        _event.RemoveEventHandler(_eventContainer, eventHandler);
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,当我注册到 Windows 推送通知服务时,我使用它来等待从 HttpNotificationChannel 获取 Url。

            HttpNotificationChannel pushChannel = new HttpNotificationChannel(channelName);
            //ChannelUriUpdated is event 
            EventWaiter<NotificationChannelUriEventArgs> ew = new EventWaiter<NotificationChannelUriEventArgs>(pushChannel, "ChannelUriUpdated");
            pushChannel.Open();
            ew.WaitForEvent(TimeSpan.FromSeconds(30));
Run Code Online (Sandbox Code Playgroud)