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





Если это единственный обработчик, вы можете проверить, имеет ли событие значение null, если нет, обработчик добавлен.
Я думаю, вы можете безопасно вызвать - = для события с вашим обработчиком, даже если он не добавлен (если нет, вы можете его поймать), чтобы убедиться, что его нет, перед добавлением.
Эта логика сломается, как только событие будет обработано где-то еще.
EventHandler.GetInvocationList().Length > 0
разве это не бросает, когда список == null?
вне класса, владеющего обработчиком событий, вы можете использовать только - = и + =. вы не можете получить доступ к событию.
За пределами определяющего класса, как упоминает @Telos, вы можете использовать EventHandler только с левой стороны += или -=. Итак, если у вас есть возможность изменить определяющий класс, вы можете предоставить метод для выполнения проверки, проверив, является ли обработчик событий 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.
Однако вам может быть лучше пересмотреть способ ввода в эксплуатацию и вывода из эксплуатации этих объектов, чтобы увидеть, не сможете ли вы найти способ самостоятельно отслеживать эту информацию.
Это не компилируется, EventHandler может находиться только слева от + = или - =.
После дальнейших объяснений голосование "против" отменено. Состояние SQL в значительной степени разрушает всю идею здесь ... :(
Спасибо, Блэр и SO search, именно то, что я искал (раздражает, что вы не можете делать это вне класса)
На самом деле, это ОТЛИЧНЫЙ ответ, который решил очень сложную ошибку в моем коде. Благодаря тонну!
Проблема возникает в большинстве случаев при сравнении делегатов на равенство. Поэтому используйте Delegate.Equals(objA, objB), если хотите проверить наличие точно такого же делегата. В противном случае сравните свойства по отдельности, как if (objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Этот код не работает в WinForm. Это строго для ASP.NET?
@Sanjay Вы правы и избавили меня от множества поисков, поскольку код Блэра выдает предупреждение о возможном сравнении ссылок.
Вместо этого уродливого цикла лучше выглядел бы метод «Содержит». ((IList) EventHandler.GetInvocationList ()). Содержит (prospecti veHandler)
public static bool IsRegistered (этот обработчик EventHandler, Delegate prospectiveHandler) => обработчик! = null && handler.GetInvocationList (). Any (existingHandler => existingHandler == prospectiveHandler);
В этом примере показано, как использовать метод 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 делегата, чтобы узнать, добавлена ли конкретная функция.
Если вы хотите узнать, прикреплен ли он только один, вы можете просто проверить значение null.
GetInvocationList () не является членом моего класса. Фактически, я не могу найти этот метод ни для одного объекта или обработчика, к которому у меня есть доступ ...
Я тоже пробовал это, по-видимому, вы можете получить доступ к такому событию только изнутри класса. Я делаю это в общем, и, как уже упоминали другие, обработчики событий, вероятно, все равно теряются. Благодарю за разъяснение!
Если я правильно понимаю вашу проблему, у вас могут быть проблемы посерьезнее. Вы сказали, что другие объекты могут подписаться на эти события. Когда объект сериализуется и десериализуется, другие объекты (те, над которыми вы не контролируете) потеряют свои обработчики событий.
Если вас это не беспокоит, то достаточно будет ссылки на обработчик событий. Если вас беспокоят побочные эффекты потери обработчиков событий другими объектами, возможно, вам стоит пересмотреть свою стратегию кэширования.
Ооо! Даже не подумал об этом ... хотя это должно было быть очевидным, учитывая мою первоначальную проблему, заключалась в том, что мой собственный обработчик терялся.
Недавно я столкнулся с подобной ситуацией, когда мне нужно было зарегистрировать обработчик события только один раз. Я обнаружил, что вы можете сначала безопасно отменить регистрацию, а затем снова зарегистрироваться, даже если обработчик вообще не зарегистрирован:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
Обратите внимание, что выполнение этого действия каждый раз при регистрации обработчика гарантирует, что обработчик будет зарегистрирован только один раз. Для меня это неплохая практика :)
Кажется рискованным; если событие запускается после удаления обработчика и до его повторного добавления, оно будет пропущено.
Конечно. Вы имеете в виду, что это не потокобезопасный Но это может быть проблемой только при запуске нескольких потоков и т.п., что необычно. В большинстве случаев этого должно быть достаточно для простоты.
Я думаю, что это лучший способ сделать это, просто попробуйте каждый раз, когда вы добавляете его, все равно вычитаете, тогда вы можете быть в большей безопасности. иначе вы будете биться головой о стену, обнаружив, что тот же метод вызывается дважды
Меня это беспокоит. Тот факт, что вы в настоящее время явно не создаете потоки в своем коде, не означает, что нет нескольких потоков или что они не будут добавлены позже. Как только вы (или кто-то другой в команде, возможно, через несколько месяцев) добавляете рабочий поток или отвечаете как на пользовательский интерфейс, так и на сетевое соединение, это открывает дверь для очень прерывистых сброшенных событий.
Я считаю, что временной интервал между удалением и повторным добавлением настолько мал, что вряд ли будут пропущенные события, но да, это все еще возможно.
Если вы используете это для чего-то вроде обновления пользовательского интерфейса и т. д., Тогда это такая тривиальная задача, риск в порядке. Если бы это было для обработки сетевых пакетов и т. д., Я бы не стал его использовать.
@alf: В веб-приложении это не только обычно, но и в однопользовательском режиме.
Всегда можно синхронизировать. Приостановите запуск события до завершения удаления / добавления.
Это не работает с некоторыми обработчиками событий, такими как: MediaPlayer.Changed -= Player_Changed; Выдает исключение Handler has not been registered with this event.
не работает с асинхронным режимом
Я согласен с ответом Альфа, но небольшие изменения в нем есть, использовать,
try
{
control_name.Click -= event_Click;
main_browser.Document.Click += Document_Click;
}
catch(Exception exce)
{
main_browser.Document.Click += Document_Click;
}
Единственный способ, который сработал для меня, - это создать логическую переменную, для которой я установил значение true при добавлении события. Затем я спрашиваю: если переменная ложна, я добавляю событие.
bool alreadyAdded = false;
Эта переменная может быть глобальной.
if (!alreadyAdded)
{
myClass.MyEvent += MyHandler;
alreadyAdded = true;
}
см. также stackoverflow.com/questions/367523/…