Чи додано обробник подій?


183

Чи є спосіб визначити, чи додано обробник події до об'єкта? Я серіалізую перелік об’єктів у стан сесії та виходжу з нього, щоб ми могли використовувати стан сеансу на базі SQL ... Коли об’єкт у списку змінено властивість, його потрібно позначити, про що обробник подій перед цим належним чином подбав . Однак тепер, коли об'єкти дезаріалізовані, це не отримує обробник події.

Під час легкого роздратування я щойно додав обробник подій до властивості Get, яка отримує доступ до об'єкта. Зараз це називається, що чудово, за винятком того, що його називають як 5 разів, тому я думаю, що обробник просто додає кожен раз, коли доступ до об'єкта.

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

Це можливо?

EDIT: Я не обов'язково повністю контролюю, які додані обробники подій, тому просто перевірити наявність null недостатньо добре.


Відповіді:


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 в значній
мірі

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

212

Нещодавно я потрапив у подібну ситуацію, коли мені потрібно було лише один раз зареєструвати обробника для події. Я виявив, що ви можете спочатку скасувати реєстрацію, а потім знову зареєструватися, навіть якщо обробник не зареєстрований зовсім:

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

Зауважте, що робити це кожен раз, коли ви реєструєте обробника, гарантуєте, що ваш обробник зареєстрований лише один раз. Мені це здається досить гарною практикою :)


9
Здається ризикованим; якщо подію буде знято після видалення обробника та перед тим, як додати її назад, вона буде пропущена.
Джиммі

27
Звичайно. Ви маєте на увазі, що це не безпечно для ниток. Але це може бути проблемою лише при запуску декількох потоків тощо, що не є звичайним. У більшості випадків це повинно бути досить добре для простоти.
alf

6
Це мене турбує. Тільки тому, що ви наразі не створюєте явно потоки у своєму коді, це не означає, що не існує декількох потоків або що вони не будуть додані пізніше. Як тільки ви (або хтось із інших членів команди, можливо, місяці пізніше) додаєте робочу нитку або відповідаєте як на користувальницький інтерфейс, так і на мережеве підключення, це відкриває двері для сильно переривчастих подій, що випали.
Технофіл

1
Я вважаю, що часове вікно між видаленням та додаванням знову настільки мало, що навряд чи існують пропущені події, але так, все-таки це можливо.
Аліссон

2
Якщо ви використовуєте це для чогось подібного до оновлення інтерфейсу користувача тощо, тоді його така тривіальна задача ризик у порядку. Якби це було для обробки мережевих пакетів тощо, я б не використовував його.
рулони

18

Якщо це єдиний обробник, ви можете перевірити, чи подія є нульовою, чи ні, обробник додано.

Я думаю, що ви можете сміливо зателефонувати - = на подію зі своїм обробником, навіть якщо він не доданий (якщо ні, ви можете його зловити) - щоб переконатися, що його немає там, перш ніж додавати.


3
Ця логіка порушиться, як тільки подія буде розроблена ще десь.
помилка87

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,

Я теж спробував це, мабуть, ви можете отримати доступ до подібної події лише з класу. Я роблю це загалом, і, як інші згадували, обробники подій, швидше за все, втрачаються. Дякуємо за роз’яснення!
CodeRedick

4

Якщо я правильно розумію вашу проблему, у вас можуть виникнути більші проблеми. Ви сказали, що інші об'єкти можуть підписатися на ці події. Коли об'єкт серіалізується та десеріалізується, інші об'єкти (ті, які ви не маєте під контролем) втрачають обробники подій.

Якщо ви не переживаєте з цього приводу, то зберігання посилання на обробника подій повинно бути досить хорошим. Якщо ви турбуєтесь про побічні ефекти інших об'єктів, що втрачають обробники подій, то, можливо, вам доведеться переглянути свою стратегію кешування.


1
D'oh! Я навіть не думав про це ... хоча це повинно було бути очевидним, враховуючи, що моя первісна проблема була втратою мого власного обробника.
CodeRedick

2

Єдиний спосіб, який працював для мене, - це створити змінну Boolean, яку я встановив як true, коли додаю подію. Тоді я запитую: Якщо змінна хибна, я додаю подію.

bool alreadyAdded = false;

Ця змінна може бути глобальною.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}


0
EventHandler.GetInvocationList().Length > 0

2
це не кидає, коли список == null?
Борис Калленс

2
поза класом, що володіє обробником подій, ви можете використовувати лише - = і + =. Ви не можете отримати доступ до події.
tbergelt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.