Wzorzec C # zapobiegający dwukrotnemu podłączeniu obsługi zdarzenia [duplikat]

To pytanie ma już odpowiedź tutaj:

Duplikat: Jak upewnić się, że zdarzenie jest subskrybowane tylko raz i czy został już dodany moduł obsługi zdarzenia?

Mam Singletona, który zapewnia jakąś usługę i moje klasy podłączają się do niektórych wydarzeń na nim, czasami klasa jest podłączony dwa razy do zdarzenia, a następnie zostaje wezwany dwa razy. Szukam klasycznego sposobu, by temu zapobiec. jakoś muszę sprawdzić, czy mam już podłączony do tego wydarzenia...

Author: Community, 2009-06-02

9 answers

Jawnie zaimplementuj Zdarzenie i sprawdź listę wywołań. Musisz również sprawdzić, czy nie ma 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;
    }
}

Używając powyższego kodu, jeśli wywołujący subskrybuje Zdarzenie wiele razy, zostanie ono po prostu zignorowane.

 128
Author: Judah Himango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-11 21:31:23

Może najpierw usuniesz Zdarzenie za pomocą -= , jeśli nie zostanie znalezione, wyjątek nie zostanie wyrzucony

/// -= 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);
 153
Author: PrimeTSS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-21 22:18:49

Przetestowałem każde rozwiązanie i najlepsze (biorąc pod uwagę wydajność) jest:

private EventHandler _foo;
public event EventHandler Foo {

    add {
        _foo -= value;
        _foo += value;
    }
    remove {
        _foo -= value;
    }
}

Nie wymaga użycia Linq. Nie ma potrzeby sprawdzania wartości null przed anulowaniem subskrypcji(szczegóły w MS EventHandler). Nie musisz pamiętać, aby wypisać się wszędzie.

 24
Author: LoxLox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-20 20:15:55

Naprawdę powinieneś się tym zająć na poziomie zlewu, a nie na poziomie źródła. Oznacza to, że nie przepisuj logiki obsługi zdarzeń w źródle zdarzenia-zostaw to samym programom obsługi (zlewom).

Jako twórca serwisu, kim jesteś, żeby twierdzić, że zlewozmywaki można zarejestrować tylko raz? A jeśli z jakiegoś powodu chcą zarejestrować się dwa razy? A jeśli próbujesz poprawić błędy w zlewozmywakach poprzez modyfikację źródła, jest to kolejny dobry powód, aby poprawić te problemy w / align = "left" /

Jestem pewien, że masz swoje powody; źródło zdarzeń, dla których duplikaty są nielegalne, nie jest niezgłębione. Ale być może powinieneś rozważyć alternatywną architekturę, która pozostawia semantykę zdarzenia nienaruszoną.

 18
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-11 20:31:56

Musisz zaimplementować dodawanie i usuwanie accesorów w zdarzeniu, a następnie sprawdzić listę docelową delegata lub zapisać cele na liście.

W metodzie add możesz użyć delegata .GetInvocationList metoda uzyskiwania listy celów już dodanych do delegata.

Ponieważ delegaty są zdefiniowane jako porównaj equal, jeśli są połączone z tą samą metodą na tym samym obiekcie docelowym, prawdopodobnie możesz uruchomić tę listę i porównać, a jeśli znajdziesz żaden, który nie jest równy, dodajesz nowy.

Oto przykładowy kod, skompilowany jako aplikacja konsolowa:

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");
        }
    }
}

Wyjście:

tc_Test called

(tj. tylko raz)

 12
Author: Lasse Vågsæther Karlsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-01 23:00:44

Reactive Extensions (RX) Framework

Microsoftu może być również używany do "subskrybowania tylko raz".

Biorąc pod uwagę Zdarzenie myszy foo.Kliknij, oto jak subskrybować i otrzymywać tylko jedno zaproszenie:

Observable.FromEvent<MouseEventArgs>(foo, "Clicked")
    .Take(1)
    .Subscribe(MyHandler);

...

private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
   // This will be called just once!
   var sender = eventInfo.Sender;
   var args = eventInfo.EventArgs;
}

Oprócz funkcji "Subskrybuj raz", podejście RX oferuje możliwość komponowania zdarzeń razem lub filtrowania zdarzeń. Jest całkiem sprytny.

 6
Author: Judah Himango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-13 14:28:01

Tworzy akcję zamiast zdarzenia. Twoja klasa może wyglądać tak:

public class MyClass
{
                // sender   arguments       <-----     Use this action instead of an event
     public Action<object, EventArgs> OnSomeEventOccured;

     public void SomeMethod()
     {
          if(OnSomeEventOccured!=null)
              OnSomeEventOccured(this, null);
     }

}
 1
Author: Tono Nam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 14:35:47

Niech twój obiekt singleton sprawdzi listę osób, o których powiadamia i wywoła tylko raz, jeśli zostanie zduplikowany. Ewentualnie, jeśli to możliwe, Odrzuć żądanie załączenia zdarzenia.

 0
Author: Toby Allen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-01 22:56:48

W silverlight musisz powiedzieć e. handed = true; w kodzie zdarzenia.

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);
}
Proszę mnie zaznaczyć, jeśli to pomoże.
 0
Author: Omzig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-23 18:24:10