Перехватить переменную в EventHandler - PullRequest
1 голос
/ 09 декабря 2010

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

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

Поэтому для иллюстрации в коде у меня есть обработчик событий:

void MyEventHandler(object sender, EventArgs e){
    //Do Stuff here
}

(В качестве заметки здесь я использую базовые EventArgs, но в моей фактической реализации это специализированный подкласс, и событие объявляется с использованием универсального EventHandler)

В настоящее время я присоединяю его так:1011 *

Позднее я отключу его следующим образом:

topObject.SubObject.EventToHandle -= MyEventHandler;

Мне нужен идентификатор topObject при обработке события, поэтому я собирался изменить MyEventHandler, чтобы иметь следующую подпись:

void MyEventHandler(int id, object sender, EventArgs e)

и прикрепите обработчик событий следующим образом:

topObject.SubObject.EventToHandle += (s,e) => MyEventHandler(topObject.ID, s,e);

Меня это беспокоит в два раза.

  1. Есть ли проблема с областью, где обработчик будет фактически исчезать без удаления, когда я нахожусь вне функции, к которой он прикреплен?Я видел некоторые странные ошибки в прошлом, когда обработчик событий исчезал, когда я использовал лямбда-выражение.Не всегда, просто в некоторых случаях.Может ли кто-нибудь объяснить мне, в каких случаях это может быть, чтобы я знал, когда можно безопасно использовать синтаксис, который у меня был.
  2. Не могу точно вспомнить, но не думаю, что когда-либо удастся удалить обработчик событий, еслиЯ использую этот синтаксис, потому что созданный неявный объект не является тем же самым.

Из-за этих двух проблем я думал создать действие и сохранить действие и использовать его, пока мне не понадобилось удалитьобработчик события.Я сделал следующее:

Action<object, EventArgs> handler = (s,e) => MyEventHandler(topObject.ID, s,e);
topObject.SubObject.EventToHandle += handler;

Я понял, что действие не может быть преобразовано в обработчик событий.Есть ли какой-нибудь простой способ сделать это преобразование, которое по-прежнему будет гарантировать, что я могу отсоединить обработчик событий?Неужели я просто подумал над этим / есть ли способ, которым я не вижу сейчас, чтобы сделать это?

Ответы [ 4 ]

1 голос
/ 09 декабря 2010

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

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

0 голосов
/ 09 декабря 2010

Не меняйте подпись события. Хотя технически CLR допускает любую подпись для события, существуют причины, по которым вся платформа была разработана с событиями, имеющими подпись (object sender, EventArgs args). Фактически, существует правило FxCop для событий, которые нарушают эту подпись, CA1009: правильно объявляйте обработчики событий :

Методы обработчика событий занимают два параметры. Первый тип System.Object и называется «отправитель». Это объект, который поднял событие. Второй параметр имеет тип System.EventArgs и называется 'e'.

Существует несколько решений (альтернатив):

  • передать topObject.ID в качестве члена ваших пользовательских EventArgs.
  • создайте объект-оболочку, который инкапсулирует topObject.ID, и подключите обработчик событий к методу этого объекта.
  • использовать область закрытия, которая может сохранить ссылку на topObject.ID в области (которая идентична методу, описанному выше, но тяжелый перенос выполняется компилятором)
0 голосов
/ 09 декабря 2010

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

Другими словами, ваш обработчик событий может выглядеть так:

void Handler(object sender, EventArgs e)
{
     var s = (SubObject) sender;
     int id = s.TopObject.ID;

     // do something with id...
}

Я бы оставил подписи событий в соглашении об отправителе и аргументах.

0 голосов
/ 09 декабря 2010

Подпись обработчика события в классе, вызывающем событие, должна быть:

protected void OnMyEvent(object sender, EventArgs  e)
{
    ....
}

или

protected void OnMyEvent(object sender, MyEventArgs  e)
{
    ....
}

В этом случае вызывающая сторона будет выполнять такой код:

topObject.SubObject.MyEvent -= OnSubObjectMyEvent;

и реализовать OnSubObjectMyEvent следующим образом (пример):

private void OnSubObjectMyEvent(object sender, MyEventArgs e)
{
   int topObjectId = ((SubObjectType)sender).TopObject.Id;
   ...
}

Здесь я предполагаю, что SubObject имеет свойство TopObject, которое позволяет мне получить идентификатор верхнего объекта.способ работы большинства классов .NET Framework.Что-то не так с этим подходом?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...