2016-08-03 23 views
0

Warum ist der Garbage-Collected-Event-Handler im folgenden Beispiel?
Ich würde erwarten, dass Ereignis nach Garbage Collection empfangen wird, aber es ist nicht.
Frage geht nicht über WeakEventManager.Warum wird dieser schwache Verweis auf Ereignishandlerabfälle erfasst?

class WeakEventTest 
{ 
    public static void Run() { 
     EventConsumer ec = new EventConsumer(); 
     WeakEvent<EventArgs> weakEvent = new WeakEvent<EventArgs>(); 
     EventHandler<EventArgs> eh = ec.HandleEvent; 
     weakEvent += new WeakReference<EventHandler<EventArgs>>(ec.HandleEvent); 

     Console.WriteLine("Calling trigger"); 
     weakEvent.Trigger(null, EventArgs.Empty); 

     Console.WriteLine("Calling System.GC.Collect"); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.WaitForFullGCComplete(); 
     GC.Collect(); 

     // event handler not called here 
     Console.WriteLine("Calling trigger"); 
     weakEvent.Trigger(null, EventArgs.Empty); 
    } 


} 

class EventConsumer 
{ 
    public void HandleEvent(object obj, EventArgs args) 
    { 
     Console.WriteLine("EventReceived"); 
    } 
} 

public class WeakEvent<T> 
{ 
    private List<WeakReference<EventHandler<T>>> referenceList = new List<WeakReference<EventHandler<T>>>(); 
    private EventHandler<T> handler = null; 

    public static WeakEvent<T> operator +(WeakEvent<T> a, EventHandler<T> b) 
    { 
     lock (a.referenceList) 
     { 
      a.handler += b; 
     } 
     return a; 
    } 

    public static WeakEvent<T> operator +(WeakEvent<T> a, WeakReference<EventHandler<T>> b) 
    { 
     lock (a.referenceList) 
     { 
      a.referenceList.Add(b); 
     } 
     return a; 
    } 

    public static WeakEvent<T> operator -(WeakEvent<T> a, EventHandler<T> b) 
    { 
     lock (a.referenceList) 
     { 
      for (int i = a.referenceList.Count - 1; i >= 0; i--) 
      { 
       WeakReference<EventHandler<T>> wr = a.referenceList[i]; 
       EventHandler<T> target; 
       if (!wr.TryGetTarget(out target)) 
       { 
        a.referenceList.RemoveAt(i); 
        continue; 
       } 
       if (Object.ReferenceEquals(target, b)) 
       { 
        a.referenceList.RemoveAt(i); 
        break; 
       } 
      } 
      a.handler -= b; 
     } 
     return a; 
    } 

    public void Trigger(object obj, T args) 
    { 
     lock (referenceList) 
     { 
      for (int i = referenceList.Count - 1; i >= 0; i--) 
      { 
       WeakReference<EventHandler<T>> wr = referenceList[i]; 
       EventHandler<T> target; 
       if (!wr.TryGetTarget(out target)) 
       { 
        referenceList.RemoveAt(i); 
        continue; 
       } 
       target(obj, args); 
      } 

      if (handler != null) 
      { 
       handler(obj, args); 
      } 
     } 
    } 

    public WeakEvent<T> AddWeakHandler(EventHandler<T> b) 
    { 
     lock (referenceList) 
     { 
      referenceList.Add(new WeakReference<EventHandler<T>>(b)); 
     } 
     return this; 
    } 

Ausgabe in der Konsole ist:
Aufruf Trigger
EventReceived
Aufruf System.GC.Collect
Trigger aufrufen
-> hier würde ich EventReceived erwarten

Im folgenden einfachen Beispiel Referenz Es wird kein Müll gesammelt und funktioniert wie erwartet.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var ec = new EventConsumer(); 
     var wr = new WeakReference<EventHandler<EventArgs>>(ec.EventReceived); 

     EventHandler<EventArgs> target; 
     if (wr.TryGetTarget(out target)) 
     { 
      Console.WriteLine("Raising event"); 
      target(null, EventArgs.Empty); 
     } 

     Console.WriteLine("Calling System.GC.Collect"); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.WaitForFullGCComplete(); 
     GC.Collect(); 

     EventHandler<EventArgs> target2; 
     if (wr.TryGetTarget(out target2)) 
     { 
      Console.WriteLine("Raising event"); 
      target2(null, EventArgs.Empty); 
     } 

     Console.ReadKey(); 
    } 
} 

public class EventConsumer 
{ 
    public void EventReceived(object obj, EventArgs args) 
    { 
     Console.WriteLine("EventReceived"); 
    } 
} 
+0

Das ist was "schwach" bedeutet. Ihr zweites Snippet ist nicht ziemlich schwach genug, wenn Sie [einen Debugger verwenden] (http://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net/17131389#17131389). Führen Sie den Code so aus, wie er auf dem Computer Ihres Benutzers ausgeführt wird. Wechseln Sie zum Release-Build und wählen Sie Extras> Optionen> Debugging> Allgemein> deaktivieren Sie "JIT-Optimierung unterdrücken". –

Antwort

0

Die erste ist das erwartete Ergebnis: Sie ec nur mit schwachen Referenzen sind Referenzierung, so hat es keinen Grund, nicht gesammelt werden.

Das zweite Beispiel ist subtiler: ec wird am Leben gehalten, weil Sie eine Referenz auf target halten (die wiederum verweist ec). Nur klar, dass Verweis und Sie werden das gleiche Verhalten wie das erste Beispiel beobachten:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var ec = new EventConsumer(); 
     var wr = new WeakReference<EventHandler<EventArgs>>(ec.EventReceived); 

     EventHandler<EventArgs> target; 
     if (wr.TryGetTarget(out target)) 
     { 
      Console.WriteLine("Raising event"); 
      target(null, EventArgs.Empty); 
     } 

     // Clear the reference 
     target = null; 

     Console.WriteLine("Calling System.GC.Collect"); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.WaitForFullGCComplete(); 
     GC.Collect(); 

     EventHandler<EventArgs> target2; 
     if (wr.TryGetTarget(out target2)) 
     { 
      Console.WriteLine("Raising event"); 
      target2(null, EventArgs.Empty); 
     } 

     Console.ReadKey(); 
    } 
} 

public class EventConsumer 
{ 
    public void EventReceived(object obj, EventArgs args) 
    { 
     Console.WriteLine("EventReceived"); 
    } 
} 

Beachten Sie, dass Sie im Release-Modus kompilieren müssen. Im Debug-Modus werden Objekte bis zum Ende der Methode am Leben gehalten (selbst wenn sie nicht mehr referenziert sind), um das Debuggen zu erleichtern.

+0

Ich habe vermisst, dass ec.EventReceived ist etwas wie Zeiger auf Funktion und ich hatte nur schwachen Verweis auf ec.EventReceived. Die Funktion war immer noch vorhanden (ich hatte einen starken Bezug auf ec), aber der Zeiger war Müll gesammelt. Habe ich recht? –