2016-07-08 16 views
6

Direttamente al codicestrano effetto di C# chiusure su 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(); 
     }); 
    } 
} 

L'uscita che ricevo da questo codice è

False 
True 

Come diavolo è possibile?

P.S. Sono venuto in questo mentre cercavo di capire come funziona WeakEventManager.

risposta

6

Il compilatore memorizzerà il delegato _noClosure in un campo statico e lo riutilizzerà ogni volta che si chiama Init. La stessa identica istanza può essere riutilizzata ogni volta.

Confrontare quello con _closure, che si chiude su una nuova istanza di C() su ogni chiamata a Init() - che non può essere memorizzato nella cache.

La memorizzazione nella cache di _noClosure significa che è presente un riferimento forte (il campo) al delegato, quindi non può essere sottoposto a Garbage Collection.

Se si esegue ildasm sull'applicazione, è possibile vedere tutto questo in azione.

+0

Si tratta di un dettaglio di implementazione, o che il comportamento è indicato (implicito) nella documentazione da qualche parte? – ironic

+3

@ironic: il fatto che * * * avvenga è un dettaglio di implementazione. Il fatto che * possa * accadere è nelle specifiche. (Dice che lo stesso delegato può essere riutilizzato, comunque). –