Обработчик событий уже добавлен?

183

Есть ли способ узнать, был ли обработчик события добавлен к объекту? Я сериализую список объектов в / из состояния сеанса, чтобы мы могли использовать состояние сеанса на основе SQL ... Когда у объекта в списке изменилось свойство, его необходимо пометить, о чем должным образом заботился обработчик события , Однако теперь, когда объекты десериализованы, они не получают обработчик событий.

В приступе легкого раздражения я просто добавил обработчик событий в свойство Get, которое обращается к объекту. Он вызывается сейчас, и это здорово, за исключением того, что он вызывается примерно 5 раз, поэтому я думаю, что обработчик просто добавляется каждый раз, когда к объекту обращаются.

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

Это возможно?

РЕДАКТИРОВАТЬ: я не обязательно имею полный контроль над тем, какие обработчики событий добавляются, поэтому просто проверка на ноль не достаточно хорошо.

CodeRedick
источник
смотри также stackoverflow.com/questions/367523/...
Ian Рингроуза

Ответы:

123

За пределами определяющего класса, как упоминает @Telos, вы можете использовать EventHandler только в левой части a +=или a -=. Итак, если у вас есть возможность изменить определяющий класс, вы можете предоставить метод для выполнения проверки, проверив, является ли обработчик события null- если да, то обработчик события не был добавлен. Если нет, то, возможно, и вы можете просмотреть значения в Delegate.GetInvocationList . Если один равен делегату, который вы хотите добавить в качестве обработчика события, то вы знаете, что он есть.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

И это можно легко изменить, чтобы он стал «добавить обработчик, если его там нет». Если у вас нет доступа к внутренностям класса, который выставляет событие, вам, возможно, придется изучить -=и +=, как предлагает @Lou Franco.

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

Блэр Конрад
источник
7
Это не компилируется, EventHandler может быть только в левой части + = или - =.
CodeRedick,
2
Убрал голосование при дальнейших объяснениях. Состояние SQL в значительной степени разрушает всю идею здесь ... :(
CodeRedick
1
Спасибо Блэру и ТАКому поиску, именно то, что я искал (раздражает, что вы не можете сделать это вне класса)
Джордж Мауэр
3
Проблема возникает большую часть времени при сравнении делегатов на равенство. Поэтому используйте, Delegate.Equals(objA, objB)если вы хотите проверить наличие точно такого же делегата. В противном случае сравнить свойства индивидуально, как if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Санджай
2
Этот код не работает в WinForm. Это строго для ASP.NET?
jp2code
213

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

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

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

Альф
источник
9
Кажется рискованным; если событие вызывается после удаления обработчика и до его добавления, оно будет пропущено.
Джимми
27
Конечно. Вы имеете в виду, что это не потокобезопасно. Но это может быть проблемой только при запуске нескольких потоков и т. П., Что не является обычным делом. В большинстве случаев этого должно быть достаточно для простоты.
Альф
7
Это беспокоит меня. То, что вы в настоящее время явно не создаете потоки в своем коде, не означает, что нет нескольких потоков, или что они не будут добавлены позже. Как только вы (или кто-то еще в команде, возможно, несколько месяцев спустя) добавляете рабочий поток или отвечаете как на пользовательский интерфейс, так и на сетевое подключение, это открывает дверь для крайне прерывистых пропущенных событий.
Технофил
1
Я полагаю, что временной интервал между удалением и добавлением снова настолько мал, что вряд ли будут существовать пропущенные события, но да, это все еще возможно.
Alisson
2
Если вы используете это для чего-то вроде обновления пользовательского интерфейса и т. Д., Тогда это такая тривиальная задача, риск в порядке. Если бы это было для обработки сетевых пакетов и т. Д., То я бы не использовал его.
катится
18

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

Я думаю, что вы можете смело вызывать - = для события с вашим обработчиком, даже если оно не добавлено (если нет, вы можете его перехватить) - чтобы убедиться, что его там нет, прежде чем добавлять.

Лу Франко
источник
3
Эта логика сломается, как только событие будет обработано где-то еще.
bugged87
6

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

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

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

Если вы хотите посмотреть, подключен ли он только один, вы можете просто проверить на ноль.

Джейсон Джексон
источник
GetInvocationList () не является членом моего класса. На самом деле, я не могу найти этот метод ни для какого объекта или обработчика, к
которому у
Я тоже это попробовал, видимо, вы можете получить доступ к событию только изнутри класса. Я делаю это в общем, и, как уже упоминали другие, обработчики событий, вероятно, в любом случае теряются. Спасибо за разъяснения!
CodeRedick
4

Если я правильно понимаю вашу проблему, у вас могут быть большие проблемы. Вы сказали, что другие объекты могут подписаться на эти события. Когда объект сериализуется и десериализуется, другие объекты (те, которыми вы не управляете) теряют свои обработчики событий.

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

CodeChef
источник
1
D'о! Я даже не думал об этом ... хотя это должно было быть очевидно, учитывая, что моей первоначальной проблемой было потерять мой собственный обработчик.
CodeRedick
2

Я согласен с ответом Альфа, но мало что можно изменить, это использовать,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }
Разработчик программного обеспечения
источник
2

Единственный способ, который сработал для меня, - это создание логической переменной, которую я установил в true, когда добавляю событие. Затем я спрашиваю: если переменная ложна, я добавляю событие.

bool alreadyAdded = false;

Эта переменная может быть глобальной.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}
Xtian11
источник
0
EventHandler.GetInvocationList().Length > 0
benPearce
источник
2
это не бросить, когда список == ноль?
Борис Калленс
2
вне класса, владеющего обработчиком событий, вы можете использовать только - = и + =. Вы не можете получить доступ к событию.
Тбергельт