2015-09-04 9 views
11

zastanawiałem się czy warto byłoby wdrożenie słabych imprez (gdzie są one właściwe) przy użyciu coś jak poniżej (Gorsza kodĂłw):Czy dobrze jest zaimplementować zdarzenie C# ze słabym odwołaniem pod maską?

class Foo { 

    private WeakEvent<EventArgs> _explodedEvent = new WeakEvent<EventArgs>(); 

    public event WeakEvent<EventArgs>.EventHandler Exploded { 
     add { _explodedEvent += value; } 
     remove { _explodedEvent -= value; } 
    } 

    private void OnExploded() { 
     _explodedEvent.Invoke(this, EventArgs.Empty); 
    } 

    public void Explode() { 
     OnExploded(); 
    } 

} 

pozwalając innym klasy do subskrybowania i wypisać z wydarzeń tym bardziej konwencjonalny C# składni podczas gdy pod maską faktycznie realizowane ze słabymi odniesieniami:

static void Main(string[] args) { 
    var foo = new Foo(); 
    foo.Exploded += (sender, e) => Console.WriteLine("Exploded!"); 

    foo.Explode(); 
    foo.Explode(); 
    foo.Explode(); 

    Console.ReadKey(); 
} 

gdzie klasa WeakEvent<TEventArgs> pomocnik jest zdefiniowany następująco:

public class WeakEvent<TEventArgs> where TEventArgs : EventArgs { 

    public delegate void EventHandler(object sender, TEventArgs e); 

    private List<WeakReference> _handlers = new List<WeakReference>(); 

    public void Invoke(object sender, TEventArgs e) { 
     foreach (var handler in _handlers) 
      ((EventHandler)handler.Target).Invoke(sender, e); 
    } 

    public static WeakEvent<TEventArgs> operator + (WeakEvent<TEventArgs> e, EventHandler handler) { 
     e._handlers.Add(new WeakReference(handler)); 
     return e; 
    } 

    public static WeakEvent<TEventArgs> operator - (WeakEvent<TEventArgs> e, EventHandler handler) { 
     e._handlers.RemoveAll(x => (EventHandler)x.Target == handler); 
     return e; 
    } 

} 

Czy to dobre podejście? czy są jakieś niepożądane efekty uboczne tego podejścia?

+2

Czy wyrejestrowany WeakEventManager? https://msdn.microsoft.com/en-us/library/vstudio/aa970850(v=vs.100).aspx – lintmouse

+0

ruch do http://codereview.stackexchange.com? – quetzalcoatl

+2

@quetzalcoatl no, i do zbliżających się wyborców: nie. To jest dobre dla tej witryny. – AgentFire

Odpowiedz

3

That's a bad idea ponieważ:

  1. Twój program zaczyna być niedeterministyczny, ponieważ skutki uboczne zależą od działań GC.
  2. GChodzą kosztem wydajności.

Zobacz powiązaną odpowiedź. Jest to 95% duplikat, ale nie na tyle, aby zamknąć pytanie, które myślę. Ja przytoczę najistotniejsze elementy:


Nie

również jest różnica semantyczna i non-determinizm, które byłyby spowodowane przez słabe referencje. Jeśli podłączysz () => LaunchMissiles() do jakiegoś wydarzenia, możesz czasem znaleźć pociski, które można wystrzelić. Innym razem GC już zabrał handlerowi. Można to rozwiązać za pomocą dependent handles, które wprowadzają jeszcze jeden poziom złożoności.

ja osobiście uważam to rzadkie, że silny adresowe charakter wydarzenia jest problem. Często zdarzenia są łączone między obiektami, które mają to samo lub bardzo podobne życie. Na przykład można podłączyć do wszystkich zdarzeń, które mają w kontekście żądania HTTP w ASP.NET ponieważ wszystko będzie kwalifikował się do zbierania, gdy wniosek został zakończony. Wszelkie wycieki są ograniczone i krótkotrwałe.

2

Kilka uwag o konkretnej realizacji:

  1. Sprawdź wartość handler.Target dla null przed wywołaniem go tak, że nie starają się zrobić z obiektem, który został umieszczony.

  2. C# ma specjalne zasady dostępu, w jaki sposób można używać zdarzeń. Nie możesz wykonać a.Event1 = a.Event2 + SomeOtherMethod, chyba że ten kod ma prywatny dostęp do wydarzeń. Jest to jednak dozwolone dla delegatów. Twoja implementacja zachowuje się bardziej jak delegat zamiast zdarzenia. Nie jest to prawdopodobnie główny problem, ale coś do przemyślenia.

  3. Twoje metody operatora powinny zwrócić nowy obiekt zamiast modyfikować pierwszy argument i zwracać go. Implementacja operator + pozwala na składnię taką jak: a = b + c, ale w twojej implementacji modyfikujesz stan b! To nie jest koszerne, bo można by oczekiwać, że operatorzy będą działać; musisz zwrócić nowy obiekt zamiast modyfikować istniejący. (Również z tego powodu implementacja nie jest bezpieczny wątku operatora Calling + z jednego wątku, podczas gdy inny było podniesienie zdarzenie podniesie wyjątek, ponieważ kolekcja została zmodyfikowana podczas foreach..)

+0

Uzgodniona rzecz operatora; tak jak powiedziałem; to po prostu super szorstkie; Myślę, że zamiast tego użyłbym metod Dodaj i Usuń. Moje pytanie jest bardziej skierowane w stronę; czy w ogóle jest to ogólnie dobre podejście? –

Powiązane problemy