2016-04-21 6 views
2

ich schwach Ereignisse bin mit, wenn ich nicht deterministisch abmelden (sonst würde ich += und -= statt schwacher Veranstaltung bevorzugen):Schwache Ereignisse und GC

class SomeType 
{ 
    public SomeType(...) 
    { 
     // object doesn't know when it will be removed 
     WeakEventManager(SomeSource, EventArgs).AddHandler(someSourceInstance, 
      nameof(SomeSource.SomeEvent), (s, e) => { ... }); 
    } 
} 

Auf diese Weise, wenn das Objekt Müll gesammelt wird, dann Ereignis Handler wird nicht aufgerufen. Perfekt.

Jedoch. Wenn das Objekt noch nicht Garbage Collection ist (aber keine starken Referenzen mehr vorhanden sind), wird der Event-Handler trotzdem aufgerufen.

Meine Frage ist eher allgemein: Was soll ich tun, wenn schwache Ereignisse verwendet werden? Sollte ich erwarten, ungültiger Aufruf im Event-Handler bei der Verwendung von schwachen Ereignissen? Oder sollte ich force GC diesen Fall vermeiden (Art von deterministischem "Aufräumen")? Etwas anderes?

+1

Sie haben es rückwärts - schwache Ereignisse erlauben Abonnenten, Garbage Collected zu sammeln, wenn sie nur durch Event-Handler am Leben erhalten werden. Sie sind nicht da, um die Event-Handler loszuwerden, nur weil der Abonnent für GC berechtigt ist, das ist ein Nebeneffekt. Sie können das als "ungültigen Anruf" bezeichnen, aber das ist es nicht. Die kurze Antwort ist also "Ja, Sie sollten solche Anrufe erwarten"; desto länger ist "was machst du überhaupt, dass dies ein Problem ist, und fehlt dir Code, der deine Absichten explizit machen kann"? –

+0

@ JeroenMostert, ich hatte Speicherverlust wegen fehlenden '- ='. Es war nicht * einfach * (ich habe nicht * unmöglich * gesagt), sich abzumelden, deshalb habe ich versucht, schwache Ereignisse zu verwenden. Sie lösen Memory Leakage Problem perfekt, aber ein anderes Problem entsteht ... daher meine Frage. Wie soll ich Absicht machen? "Dieses Objekt benötigt diese Methode um klar zu sein? 'IDisposable'? – Sinatr

+1

'IDisposable' ist eine Möglichkeit, ja. Es ist der Hauptmechanismus, den C# für die deterministische Säuberung hat. Obwohl es technisch nur für die Freigabe nicht verwalteter Ressourcen vorgesehen ist, wird es häufig in Frameworks für die deterministische Bereinigung verwendet, selbst wenn keine nicht verwalteten Ressourcen beteiligt sind. Es hat den Vorteil der Sprachunterstützung ('using') und signalisiert dem Entwickler eindeutig, dass dieses Objekt explizit freigegeben werden soll. Noch besser als "IDisposable" ist es jedoch, die Eigentumsregeln für Ihre Objekte zu ermitteln und die Eigentümer dazu zu bringen, das Objekt, wenn überhaupt möglich, "herunterzufahren". –

Antwort

3

Sie sollten immer erwarten, dass ein Event-Handler nach der Registrierung auch bei "starken" Ereignissen aufgerufen wird. Es ist nichts Ungültiges an einem solchen Anruf.

Das einfachste Szenario ist offensichtlich, wenn man sich anschaut, wie Event-Handler ausgeführt:

protected void OnMyEvent(object sender, EventArgs e) 
{ 
    var ev = MyEvent; 
    if (ev != null) ev(this, EventArgs.Empty); 
} 

Wenn ein Delegierter zwischen ev = MyEvent und ev.Invoke nicht registriert ist, wird es noch die Benachrichtigung zum frühestmöglichen Zeitpunkt erhalten. Habe ich schon erwähnt, dass das gleichzeitige Programmieren noch schwer ist?

Aber in Ihrem Fall ist das Problem wirklich "Warum weiß das Objekt nicht, wann die Registrierung aufgehoben werden soll?" Beantworte das und du wirst deine Lösung haben. Warum sollte ein Ereignishandler aufgerufen werden, der auf ein Objekt verweist, das nicht mehr stark referenziert ist, wenn es sich um eine unzulässige Operation handelt? Es ist nicht so, als wäre das Objekt teilweise gesammelt oder irgendetwas - es wurde einfach noch nicht gesammelt.

+0

Es gibt kein zu verwendendes Muster. 'IDisposable'? Nur abmelden? In 'C++' gibt es Destruktoren, auf die Sie sich verlassen. Ich kann eine Methode 'CallMeWhenYouDontNeedInstance()' machen, aber ich bin nicht sicher (noch kann ich sicherstellen, dass ein Benutzer eines solchen Objekts es aufruft). Dies war der Hauptpunkt der Verwendung von schwachen Ereignissen: um solche Komplikationen zu vermeiden. – Sinatr

+0

@Sinatr Sie können sich nicht mehr auf Destruktoren in C++ verlassen, als Sie sich darauf verlassen können, dass jemand Ihre Aufräummethode aufruft, sei es "Dispose" (was pedantisch falsch wäre) oder nicht. In fast allen Fällen sollten Objekte eindeutige Eigentumsrechte haben und wenn der Eigentümer fertig ist, sollte er das Objekt, über das er verfügt, entsorgen. Ich verstehe, was Sie versuchen und warum, aber im Allgemeinen ist diese Art von Muster schwer zu folgen und wird anfällig für Fehler, egal wie viele Hacks Sie hinzufügen ("Schau ma! Ich melde ein Ereignis in einem Finalizer! "). Wenn Sie eine Option zur Verwendung der deterministischen Bereinigung haben, tun Sie es. – Luaan

+0

Wenn die Ereignisquelle länger als das Objekt lebt, können Sie tatsächlich den Handler aufrufen, selbst wenn das Objekt * entsorgt wird * (es sei denn, Sie kündigen es aus, bevor es für GC zugelassen wird). Ich habe nicht gedacht, was mein normaler Event-Handler auch hatte, einen ungültigen Zustand zu haben. – Sinatr