C #, чтобы предотвратить перехват обработчика события дважды

Дубликат: Как обеспечить, чтобы событие было подписано только один раз и добавлен ли обработчик событий?

У меня есть singleton, который предоставляет некоторую услугу, а мои classы подключаются к некоторым событиям на нем, иногда class дважды подключается к событию, а затем дважды вызывается. Я ищу classический способ предотвратить это. почему-то мне нужно проверить, не подключен ли я к этому событию …

Явно реализовать событие и проверить список вызовов. Вам также необходимо проверить значение null:

using System.Linq; // Required for the .Contains call below: ... private EventHandler foo; public event EventHandler Foo { add { if (foo == null || !foo.GetInvocationList().Contains(value)) { foo += value; } } remove { foo -= value; } } 

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

Как просто удалить событие сначала с -= , если он не найден, исключение не выбрасывается

 /// -= Removes the event if it has been already added, this prevents multiple firing of the event ((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii); ((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii); 

Я тестировал каждое решение и лучший (учитывая производительность):

 private EventHandler _foo; public event EventHandler Foo { add { _foo -= value; _foo += value; } remove { _foo -= value; } } 

Нет Linq, если требуется. Нет необходимости проверять значение null перед отменой подписки (подробнее см. MS EventHandler). Не нужно помнить, чтобы делать запретную подписку повсюду.

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

Как разработчик сервиса, кто вы скажете, что раковины могут регистрироваться только один раз? Что, если они захотят зарегистрироваться дважды по какой-то причине? И если вы пытаетесь исправить ошибки в приемниках, изменив источник, это снова является хорошей причиной для исправления этих проблем на уровне приемника.

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

Вам необходимо реализовать добавление и удаление аксессуаров в событии, а затем проверить целевой список делегата или сохранить целевые объекты в списке.

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

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

Вот пример кода, скомпилированный как консольное приложение:

 using System; using System.Linq; namespace DemoApp { public class TestClass { private EventHandler _Test; public event EventHandler Test { add { if (_Test == null || !_Test.GetInvocationList().Contains(value)) _Test += value; } remove { _Test -= value; } } public void OnTest() { if (_Test != null) _Test(this, EventArgs.Empty); } } class Program { static void Main() { TestClass tc = new TestClass(); tc.Test += tc_Test; tc.Test += tc_Test; tc.OnTest(); Console.In.ReadLine(); } static void tc_Test(object sender, EventArgs e) { Console.Out.WriteLine("tc_Test called"); } } } 

Вывод:

 tc_Test called 

(т. е. только один раз)

Рамки Microsoft Reactive Extensions (Rx) также могут использоваться для «подписки только один раз».

Учитывая событие мыши foo.Clicked, вот как подписаться и получить только один вызов:

 Observable.FromEvent(foo, "Clicked") .Take(1) .Subscribe(MyHandler); ... private void MyHandler(IEvent eventInfo) { // This will be called just once! var sender = eventInfo.Sender; var args = eventInfo.EventArgs; } 

В дополнение к функциональности «подписаться один раз», подход RX предлагает возможность составлять события вместе или фильтровать события. Это довольно изящно.

Создайте действие вместо события. Ваш class может выглядеть так:

 public class MyClass { // sender arguments <----- Use this action instead of an event public Action OnSomeEventOccured; public void SomeMethod() { if(OnSomeEventOccured!=null) OnSomeEventOccured(this, null); } } 

попросите ваш объект singleton проверить его список того, кто он уведомляет, и только один раз, если его дублировать. Альтернативно, если возможно, отклоните запрос на вложение события.

В silverlight вам нужно сказать e.Handled = true; в коде события.

 void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { e.Handled = true; //this fixes the double event fire problem. string name = (e.OriginalSource as Image).Tag.ToString(); DoSomething(name); } 

Пожалуйста, отметьте меня, если это поможет.

Может быть, мой ответ на подобный пост поможет:

Дифференциация между событиями, вызванными взаимодействием с пользователем и моим собственным кодом

Давайте будем гением компьютера.