Oggi parlando con alcuni VB'ers mi sono reso conto di come il concetto di Delegate non sia cosi' chiaro, infatti si sono un po' meravigliati quando gli ho detto che una classe che consuma un evento non verra' recuperata dal garbage collector fino a quando la classe che genera' l'evento non sara' a sua volta distrutta oppure fino a quando la classe 'consumer' si cancellera' dalla InvocationList della classe fonte dell'evento. (e qui il fumo si alzava...)
Probabilmente il tutto deriva dal fatto che la keyword WithEvents (eredita' di VB6) maschera parecchia roba (forse troppa) agli occhi del programmatore VB mentre in C# il motivo e' piu' evidente.
Ecco una banale console app, dove e' possibile vedere la differenza di comportamento

public class Clock

{

public delegate void TickHandler(long ticks);

public event TickHandler Tick;

public void RaiseTick(){Tick(DateTime.Now.Ticks);}

}

public class ClockMonitor{

public void LogTick(long ticks){ Console.WriteLine(ticks.ToString());}

~ClockMonitor(){Console.WriteLine("Classe ClockMonitor distrutta.");

}

}

In pratica ho una classe Clock che genera un evento Tick e una classe ClockMonitor che utilizzero' come consumer dell'evento.

static void Main(string[] args)

{

Clock clk=new Clock();

ClockMonitor cm=new ClockMonitor();

clk.Tick+=new Clock.TickHandler(cm.LogTick);

clk.RaiseTick();

//clk.Tick-=new Clock.TickHandler(cm.LogTick);

//clk=null;

cm=null; //Non ci sono altri references...

System.GC.Collect();

System.GC.WaitForPendingFinalizers();

Console.ReadLine();

}

Il codice della Main() altro non fa che associare il metodo LogTick della classe ClickMonitor all'evento Tick della classe Clock e generare un evento.
Attraverso l'oggetto GC 'forzo' il garbage collector a recuperare gli oggetti orfani e, avendo impostato cm=null mi aspetterei di vedere invocato il distruttore della classe ClockMonitor ma questo non succede, perche'?, semplicemente perche' la classe Clock conserva al suo interno un reference alla classe ClockMonitor in modo da poter invocare la LogTick(), quindi non e' vero che l'oggetto ClockMonitor non ha piu' nessun reference attivo.
Per verificare quanto detto, cancelliamo LogTick() dalla InvocationList di Clock (scommentato la prima riga rossa)  e vedremo che prima della ReadLine il distruttore di ClockMonitor verra' invocato.
Lo stesso dicasi nel caso la classe Clock (e relativa InvocationList) venga distrutta (secondo commento in rosso)

Non credo di aver detto nulla di nuovo, ma capire che dietro un evento ci stanno sempre dei delegates e che questi si portano appresso dei reference puo' essere utile a spiegare come mai, a volte, quell'oggetto che consuma cosi' tante risorse, non vuole saperne di andarsene nella spazzatura... ;-)