2016-07-08 17 views
6

Gerade auf den Codemerkwürdige Wirkung von C# Schließungen auf Garbage Collector

class Program 
{ 
    private static WeakReference<EventHandler<EventArgs>> _noClosure; 
    private static WeakReference<EventHandler<EventArgs>> _closure; 

    static void Main(string[] args) 
    { 
     Init(); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.Collect(); 
     EventHandler<EventArgs> target; 
     Console.WriteLine(_closure.TryGetTarget(out target)); 
     Console.WriteLine(_noClosure.TryGetTarget(out target)); 
    } 

    class C { public void Go() { } } 

    private static void Init() 
    { 
     _noClosure = new WeakReference<EventHandler<EventArgs>>((sender, args) => 
     { 
     }); 

     var s = new C(); 
     _closure = new WeakReference<EventHandler<EventArgs>>((sender, args) => 
     { 
       s.Go(); 
     }); 
    } 
} 

Der Ausgang ich von diesem Code erhalten, ist

False 
True 

Wie auf der Erde ist das möglich?

P.S. Ich kam dazu, während ich versuchte herauszufinden, wie WeakEventManager funktioniert.

Antwort

6

Der Compiler wird den _noClosure Delegaten in einem statischen Feld zwischenspeichern und bei jedem Aufruf erneut verwenden Init. Die exakt gleiche Instanz kann jedes Mal wiederverwendet werden.

Vergleichen Sie das mit _closure, die über eine neue Instanz von C() bei jedem Anruf zu Init() schließt - das kann nicht zwischengespeichert werden.

Das Caching von _noClosure bedeutet, dass es eine starke Referenz (das Feld) für den Delegaten gibt, so dass keine Garbage Collections gesammelt werden können. Wenn Sie ildasm in Ihrer Anwendung ausführen, können Sie dies alles in Aktion sehen.

+0

Ist dies ein Implementierungsdetail, oder wird dieses Verhalten irgendwo in der Dokumentation angegeben? – ironic

+3

@ironic: Die Tatsache, dass es * passiert * ist ein Implementierungsdetail. Die Tatsache, dass es passieren kann, ist in der Spezifikation. (Es besagt, dass derselbe Delegat trotzdem wiederverwendet werden kann.) –