Как вызвать событие (особенно с анонимным обработчиком) только один раз в .NET? - PullRequest
0 голосов
/ 01 марта 2010

Я использую API на основе событий и хочу, чтобы определенный метод обработки событий вызывался только один раз. (Обратите внимание, что у меня есть другой метод, который всегда обрабатывает событие, но я хочу добавить другой обработчик для определенных ситуаций и сделать его только один раз.)

Возможно ли / рекомендуется ли отписаться от события внутри обработчика события? Э.Г.

private void OnEventRaised(object sender, EventArgs e) {
    _eventRaisingObject.EventRaised -= OnEventRaised;
    ... // Do normal code
}

Какого рода проблемы с многопоточностью следует учитывать при таком подходе?

Во-вторых, можно ли вызывать обработчик событий только один раз, когда он является анонимным методом? Э.Г.

_eventRaisingObject.EventRaised += (sender, e) => {
    // Unsubscribe?
    ... // Do normal code
}

В настоящее время я использую анонимный метод и просто проверяю логическое значение, но я думаю, что было бы лучше (с меньшими накладными расходами и потенциальными ошибками) отписаться, когда работа завершена - возможно, это предположение неверно. Одна проблема, которую я предвижу при проверке логического значения, заключается в том, что, если ситуация требует повторения (чтобы снова был добавлен анонимный обработчик событий), общий логический аргумент мог бы разрешать вызов анонимных методов несколько раз в разных потоках.

Ответы [ 4 ]

2 голосов
/ 01 марта 2010
EventHandler handler;
handler = (sender, e) => {
    _eventRaisingObject.EventRaised -= handler;

    // Do normal code
}
_eventRaisingObject.EventRaised += handler;

Должно работать из-за замыканий, захватывающих как обработчик, так и eventRaisingObject.

1 голос
/ 01 марта 2010

Отмена подписки внутри обработчика событий возможна, но здесь есть явное условие гонки. Другой поток может быть также в процессе вызова обработчика событий, поэтому получить обработчик с именем после отмены подписки вполне возможно. Вам нужно будет сериализовать доступ к делегату события:

  public event EventHandler MyEvent;
  private object eventLock = new object();
...
  protected void OnMyEvent(EventArgs e) {
    lock(eventLock) {
      var handler = MyEvent;
      if (handler != null) MyEvent(this, e);
    }
  }

Это ничем не отличается от защиты вашего логического флага, хотя вы можете сделать это намного эффективнее с помощью класса Interlocked.

1 голос
/ 01 марта 2010

Поскольку ситуация, требующая обработки события, может возникать несколько раз, и поскольку многопоточность является проблемой, я бы порекомендовал установить более тесную связь между повышением и условиями обработки.

Я бы не стал динамически добавлять и удалять обработчик событий. Конечно, это можно сделать в обработчике событий, но сделать его устойчивым в многопоточной среде будет очень сложно.

Но я определенно не использовал бы и логическое значение для этого по тем же причинам.

Вы можете рассмотреть вопрос о создании очереди. Помещая элемент в очередь, чтобы сигнализировать о том, что обработчик событий должен ответить на событие, и удаляя элемент из очереди, когда на него отвечают (с надлежащей блокировкой с обеих сторон, конечно), вы можете лучше контролировать процесс .

Может быть, это не работает в ваших обстоятельствах, но это то, что нужно учитывать.

0 голосов
/ 01 марта 2010

Вы можете привести отправителя к объекту, вызвавшему событие, и отписаться.

void myRaisingObject_EventRaised(object sender, EventArgs e)
{
  (sender as MyRaisingObject).EventRaised -= myRaisingObject_EventRaised;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...