Иногда хочется заблокировать мой поток в ожидании события.
Обычно я делаю что-то вроде этого:
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;
}
Однако, похоже, это должно быть что-то, что я мог бы извлечь из общего класса (или, может быть, даже из того, что уже существует в фреймворке).
Я хотел бы иметь возможность сделать что-то вроде этого:
EventWaiter ew = new EventWaiter(button.Click);
ew.WaitOne();
EventWaiter ew2 = new EventWaiter(form.Closing);
ew2.WaitOne();
Но я не могу найти способ создать такой класс (я не могу найти хороший действительный способ передать событие в качестве аргумента). Кто-нибудь может помочь?
Чтобы привести пример того, почему это может быть полезно, рассмотрим что-то вроде этого:
var status = ShowStatusForm();
status.ShowInsertUsbStick();
bool cancelled = WaitForUsbStickOrCancel();
if(!cancelled){
status.ShowWritingOnUsbStick();
WriteOnUsbStick();
status.AskUserToRemoveUsbStick();
WaitForUsbStickToBeRemoved();
status.ShowFinished();
}else{
status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
Это гораздо более кратко и читабельно, чем эквивалентный код, написанный с логикой, распределенной между многими методами. Но чтобы реализовать 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 */
Мне гораздо труднее читать. Следует признать, что далеко не всегда поток будет таким линейным, но когда это так, мне нравится первый стиль.
Это сравнимо с использованием ShowMessage () и Form.ShowDialog () - они также блокируются, пока не произойдет какое-либо «событие» (хотя они будут запускать цикл сообщений, если они вызваны в потоке GUI).