2008-10-17 18 views
38

Używam C#, .NET 3.5. Rozumiem, jak wykorzystać wydarzenia, jak deklarować je w mojej klasie, jak podłączyć je gdzieś indziej, itp zmyślony przykład:Jak działają zdarzenia C# za kulisami?

public class MyList 
{ 
    private List<string> m_Strings = new List<string>(); 
    public EventHandler<EventArgs> ElementAddedEvent; 

    public void Add(string value) 
    { 
     m_Strings.Add(value); 
     if (ElementAddedEvent != null) 
      ElementAddedEvent(value, EventArgs.Empty); 
    } 
} 

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     MyList tmp = new MyList(); 
     tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired); 
     tmp.Add("test"); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

Jednak to, co robię nie zrozumieć, kiedy ktoś deklaruje program obsługi zdarzeń

public EventHandler<EventArgs> ElementAddedEvent; 

Nigdy nie został zainicjowany - więc czym dokładnie jest ElementAddedEvent? Na co to wskazuje? Poniższa nie będzie działać, ponieważ Podprogram nie jest inicjowany:

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     EventHandler<EventArgs> somethingHappend; 
     somethingHappend += new EventHandler<EventArgs>(Fired); 
     somethingHappend(this, EventArgs.Empty); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

Zauważyłem, że istnieje EventHandler.CreateDelegate (...), ale wszystkie podpisy metoda sugerują ten służy wyłącznie do mocowania delegatów już istniejący EventHandler poprzez typowy ElementAddedEvent + = new EventHandler (MyMethod).

Nie jestem pewien, czy co próbuję zrobić pomoże ... ale ostatecznie chciałbym wymyślić abstrakcyjnego nadrzędnej DataContext w LINQ którego dzieci mogą zarejestrować która tabela Rodzaje chcą „obserwowanego ", więc mogę mieć zdarzenia takie jak BeforeUpdate i AfterUpdate, ale specyficzne dla typów. Coś takiego:

public class BaseDataContext : DataContext 
{ 
    private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>(); 

    public static void Observe(Type type) 
    { 
     if (m_ObservedTypes.ContainsKey(type) == false) 
     { 
      m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>()); 

      EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler); 
     } 
    } 

    public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events 
    { 
     get { return m_ObservedTypes; } 
    } 
} 


public class MyClass 
{ 
    public MyClass() 
    { 
     BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate); 
    } 

    public void OnUserUpdated(object sender, EventArgs args) 
    { 
     // do something 
    } 
} 

Myśląc o tym uświadomił mi, ja naprawdę nie rozumiem, co się dzieje pod Hod z imprez - i chciałbym zrozumieć :)

+0

Zobacz także moją odpowiedź http://stackoverflow.com/questions/2598170/question-regarding-to-value-reference-type-of-events/3854421#3854421 –

Odpowiedz

64

Pisałem to w sporo szczegółowo an article, ale tutaj jest podsumowanie, zakładając, że jesteś dość zadowolony z delegates się:

  • zdarzenie to tylko „dodatek” sposób i „usuń” sposób, w taki sam sposób, właściwość jest po prostu metodą "get" i "ustawionym" met od. (W rzeczywistości CLI pozwala również na metodę "wznoszenia/strzelania", ale C# nigdy jej nie generuje.) Metadane opisują zdarzenie z odniesieniami do metod.
  • Kiedy deklarujesz field-like event (jak twój ElementAddedEvent) kompilator generuje metody i prywatną pole (tego samego typu co delegata). Wewnątrz klasy, kiedy odnosisz się do ElementAddedEvent, odnosisz się do tego pola. Poza zajęciami odwołujesz się do pola.
  • Gdy ktoś subskrybuje zdarzenie (z operatorem + =), które wywołuje metodę dodawania. Kiedy wypisują się (za pomocą operatora - =), który wywołuje usunięcie.
  • W przypadku zdarzeń polowych istnieje pewna synchronizacja, ale w przeciwnym razie polecenie dodaj/usuń wystarczy wywołać Delegata. Combine/Remove, aby zmienić wartość pola wygenerowanego automatycznie. Obie te operacje przypisują się do pola wsparcia - pamiętaj, że delegaci są niezmienni. Innymi słowy, kod wygenerowany automatycznie jest bardzo podobny do tego:

    // Backing field 
    // The underscores just make it simpler to see what's going on here. 
    // In the rest of your source code for this class, if you refer to 
    // ElementAddedEvent, you're really referring to this field. 
    private EventHandler<EventArgs> __ElementAddedEvent; 
    
    // Actual event 
    public EventHandler<EventArgs> ElementAddedEvent 
    { 
        add 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent += value; 
          __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); 
         } 
        } 
        remove 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent -= value; 
          __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); 
         } 
        } 
    } 
    
  • Początkowa wartość pola generowanego w Twoim przypadku jest null - i zawsze będzie stać null ponownie, jeśli wszyscy abonenci są usuwane, jak to jest zachowanie Delegata. Odsuń.

  • Jeśli chcesz „no-op” obsługi do subskrybowania zdarzenia, tak aby uniknąć czek nieważności, można zrobić:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {}; 
    

    delegate {} tylko anonimowy sposób, który nie robi” t dbają o jego parametry i nic nie robią.

Jeśli jest coś, co jest nadal niejasne, proszę zapytaj, a ja postaram się pomóc!

+0

Co za miłe, zwięzłe wyjaśnienie! – DOK

+2

@ dok1: Wyjaśniłem już kilka razy wydarzenia :) Jest to jeden z najgorszych obszarów C#, IMO. –

+0

Aby upewnić się, że rozumiem poprawnie - zdarzenie przypominające pole jest faktycznie wspierane przez Delegata. Jeśli nikt nie ma + = na nim, jest zerowy (dlatego musimy sprawdzić wartość zerową). Pierwsze + = na nim wykonuje zadanie, a drugie + = łączy? – Matt

-3

Pod maską wydarzeń są tylko delegaci ze specjalnymi konwencjami telefonowania. (. Na przykład, nie trzeba sprawdzać nieważności przed podniesieniem zdarzenie)

W Pseudokod, Event.Invoke() rozkłada się tak:

Jeśli zdarzenie ma słuchaczy połączenia każdego słuchacza synchronicznie ten wątek w dowolnej kolejności.

Ponieważ zdarzenia są rozsyłane grupowo, będą miały zero lub więcej odbiorców, przechowywanych w kolekcji. CLR przejdzie przez nie, wywołując każdy w dowolnej kolejności.

Jednym wielkim zastrzeżeniem do zapamiętania jest to, że programy obsługi zdarzeń są wykonywane w tym samym temacie, w którym wydarzenie jest wywoływane. Powszechnym błędem psychicznym jest myślenie o nich jako o utworzeniu nowego wątku. Oni nie.

+8

* Musisz * sprawdzić, czy nie ma nieważności przed podniesieniem wydarzenia, chyba że jesteś ostrożny. Zdarzenia naprawdę nie są delegatami więcej niż właściwości są polami. Wydarzenia po prostu zamykają zachowanie "subscribe/unsubscribe". –

+3

Wątpię, aby kolejność połączeń była dowolna. Wierzę, że to fifo – Odys

Powiązane problemy