Timer non disposto quando la forma è

Sto cercando di capire perché un Windows.Forms.Timer non viene eliminato quando è il form che lo ha creato. Ho questa forma semplice:

 public partial class Form1 : Form { private System.Windows.Forms.Timer timer; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { timer = new Timer(); timer.Interval = 1000; timer.Tick += new EventHandler(OnTimer); timer.Enabled = true; } private void OnTimer(Object source, EventArgs e) { Debug.WriteLine("OnTimer entered"); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { this.Dispose(); } } 

Quando lo chiudo, viene chiamato this.Dispose ma l’evento di this.Dispose del timer continua a essere chiamato. Pensavo che il Dispose liberasse tutti gli oggetti di proprietà dell’object disposto. È falso? Timer ha un comportamento specifico?

Per ora, ho trovato che il modo per smaltire il timer è quello di fare timer.Tick -= OnTimer; – Lo chiamo quindi nell’evento Form1_FormClosed . È la soluzione giusta o dovrei fare diversamente?

MODIFICARE

O è semplicemente meglio fare:

 private void Form1_FormClosed(object sender, FormClosedEventArgs e) { timer.Dispose(); this.Dispose(); } 

?

Come ti ho detto nel mio precedente commento dovresti provare:

 private Form1_FormClosing(...) { timer.Stop(); timer.Tick -= new EventHandler(OnTimer); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { timer.Dispose(); timer = null; } 

Questo è buono perché previeni il timer a ricominciare (in FormClosing) e puoi controllare in altre parti (non in questo esempio perché stai chiudendo il modulo, ma come esempio) se quell’object (timer) è stato cancellato prima di usarlo .
Quindi in altre parti puoi farlo

 if (timer != null) // Note: this is false if you just use timer.Dispose() { .... } 

L’unico modo corretto per disporre i membri usa e getta di una class IDisposable è farlo all’interno del suo metodo Dispose(bool disposing) (controllare l’articolo MSDN ). In altre parole, è ansible aprire il file Form.Designer.cs generato automaticamente e inserirlo nel metodo corretto.

D’altra parte, se si aggiunge il Timer tramite VS Designer (invece di istanziarlo autonomamente), verrà aggiunto al contenitore dei components :

 // autogenerated inside Form.Designer.cs, InitializeComponent() method this.timer = new System.Windows.Forms.Timer(this.components); 

e quindi correttamente disposti quando viene eliminato il components membro:

 // autogenerated inside Form.Designer.cs, Dispose(bool disposing) method if (disposing && (components != null)) { components.Dispose(); } 

Se vuoi farlo da solo, tieni presente che il progettista non crea un’istanza dei components se non ritiene necessario. Quindi, i components potrebbero essere null nel tuo caso.

Il modo più semplice per risolverlo: aggiungi il timer trascinandolo dalla casella degli strumenti, quindi Form_Load gestore Form_Load .

  private void Form1_FormClosed(object sender, FormClosedEventArgs e) { timer.Stop(); } 

Non dovrei mettere il this.Dispose (); nell’evento di chiusura della forma basta fermare il timer.

EventHandler è il riferimento persistente, rimuove il riferimento e interrompe il timer all’evento di chiusura o non è richiesto. Se si desidera controllare che il Timer sia disposto, controllarlo nell’Evento Chiuso

Il semplice Timer.Dispose() cancella le risorse del timer, inclusa l’interruzione del timer per l’triggerszione in futuro.

Tuttavia, è ansible che dopo il reso di Dispose() , ci siano callback che sono in esecuzione o seduti triggersmente nella coda di lavoro del pool di thread in attesa di esecuzione.

Il secondo sovraccarico, Timer.Dispose(WaitHandle) segnalerà l’object passato dopo che tutte le callback per il timer sono state completate. Può trattarsi di qualsiasi WaitHandle , ad esempio ManualResetEvent .

Per semplificare le cose, puoi passare in WaitHandle.InvalidHandle e Timer.Dispose() restituirà solo quando tutte le callback sono state completate. Questo evita di dover allocare un vero object evento ed è solitamente ciò che si vuole fare.

Poiché WaitHandle è astratto, è necessario utilizzare un piccolo trucco, che è quello di creare la propria sottoclass.

 class InvalidWaitHandle : WaitHandle {} Timer tmr = new Timer(...); tmr.Dispose(new InvalidWaitHandle);