Quando dovrei creare un distruttore?


185

Per esempio:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Quando devo creare manualmente un distruttore? Quando hai avuto bisogno di creare un distruttore?


1
Il linguaggio C # chiama questi "distruttori", ma la maggior parte delle persone li chiama "finalizzatori" poiché questo è il loro nome .NET e riduce la confusione con i distruttori C ++ (che sono piuttosto diversi). Come implementare IDisposable e Finalizzatori: 3 semplici regole
Stephen Cleary,

5
Quando ti senti spericolato.
capdragon,

32
Penso che la tastiera di TomTom non funzionasse correttamente. Il blocco maiuscole si avviava e si spegneva sporadicamente. Strano.
Jeff LaFay,


Ho finito per usare un distruttore come aiuto per il debug basato sul suggerimento di Greg Beech: stackoverflow.com/questions/3832911/…
Brian

Risposte:


237

AGGIORNAMENTO: Questa domanda è stata l'oggetto del mio blog nel maggio 2015 . Grazie per l'ottima domanda! Consulta il blog per un lungo elenco di falsità che le persone credono comunemente nella finalizzazione.

Quando devo creare manualmente un distruttore?

Quasi mai.

In genere si crea un distruttore solo quando la classe si sta aggrappando ad alcune costose risorse non gestite che devono essere ripulite quando l'oggetto scompare. È meglio utilizzare il modello usa e getta per assicurarsi che la risorsa venga ripulita. Un distruttore è quindi essenzialmente una garanzia che se il consumatore dell'oggetto dimentica di smaltirlo, alla fine la risorsa verrà comunque ripulita. (Può essere.)

Se fai un distruttore fai molta attenzione e comprendi come funziona il garbage collector . I distruttori sono davvero strani :

  • Non corrono sul tuo thread; corrono sul proprio thread. Non causare deadlock!
  • Un'eccezione non gestita generata da un distruttore è una cattiva notizia. È sul suo filo; chi lo prenderà?
  • Un distruttore può essere chiamato su un oggetto dopo l'avvio del costruttore ma prima del termine del costruttore. Un distruttore correttamente scritto non farà affidamento sugli invarianti stabiliti nel costruttore.
  • Un distruttore può "resuscitare" un oggetto, rendendo di nuovo vivo un oggetto morto. È davvero strano. Non farlo
  • Un distruttore potrebbe non correre mai; non puoi fare affidamento sull'oggetto mai pianificato per la finalizzazione. E ' probabilmente sarà, ma non è una garanzia.

Quasi nulla di ciò che è normalmente vero è vero in un distruttore. Stai molto, molto attento. Scrivere un distruttore corretto è molto difficile.

Quando hai avuto bisogno di creare un distruttore?

Durante il test della parte del compilatore che gestisce i distruttori. Non ho mai avuto bisogno di farlo nel codice di produzione. Raramente scrivo oggetti che manipolano risorse non gestite.


"Un distruttore può essere chiamato su un oggetto dopo l'avvio del costruttore ma prima del termine del costruttore." Ma posso fare affidamento sugli inizializzatori di campo da eseguire, giusto?
configuratore

13
@configurator: No. Supponiamo che l'inizializzatore del terzo campo di un oggetto con un finalizzatore chiamato un metodo statico che abbia causato il lancio di un'eccezione. Quando verrebbe eseguito il quarto inizializzatore di campo? Mai. Ma l'oggetto è ancora allocato e deve essere finalizzato. Cavolo, non hai nemmeno la garanzia che i campi di tipo doppio siano stati inizializzati completamente quando il dtor viene eseguito. Potrebbe esserci stato un thread interrotto a metà della scrittura del doppio e ora il finalizzatore ha a che fare con un doppio zero a metà inizializzato.
Eric Lippert,

1
Post eccellente, ma avrei dovuto dire "dovrebbe essere creato quando la tua classe si regge su un costoso oggetto non gestito o fa esistere un gran numero di oggetti non gestiti" - Per un esempio concreto, ho una classe di matrici in C # che utilizza un C ++ nativo sottostante classe matriciale per fare molto sollevamento pesante - faccio molte matrici - un "distruttore" è di gran lunga superiore a IDisposable in questo caso specifico, perché mantiene i lati gestiti e non gestiti della casa in una migliore sincronizzazione
Mark Mullin

1
pythonnet usa il distruttore per rilasciare GIL in CPython non gestito
denfromufa,

3
Fantastico articolo Eric. Props for this -> "Divertimento bonus extra: il runtime utilizza una generazione di codice meno aggressiva e una garbage collection meno aggressiva quando si esegue il programma nel debugger, perché è una brutta esperienza di debug avere oggetti di cui si sta eseguendo il debug scompaiono improvvisamente anche se il la variabile che si riferisce all'oggetto è nell'ambito. Ciò significa che se si dispone di un bug in cui un oggetto viene finalizzato troppo presto, probabilmente non è possibile riprodurre quel bug nel debugger! "
Ken Palmer,

17

Si chiama "finalizzatore" e di solito dovresti crearne uno solo per una classe il cui stato (es: campi) include risorse non gestite (es. Puntatori a handle recuperati tramite chiamate p / invoke). Tuttavia, in .NET 2.0 e versioni successive, esiste in realtà un modo migliore per gestire la pulizia di risorse non gestite: SafeHandle . Detto questo, non dovresti praticamente mai più scrivere un finalizzatore.


25
@ThomasEding - Sì, lo è . C # usa la sintassi del distruttore, ma in realtà sta creando un finalizzatore . Anche in questo caso .
JDB ricorda ancora Monica il

@JDB: il costrutto linguistico è chiamato distruttore. Non mi piace il nome, ma è così che si chiama. L'atto di dichiarare un distruttore fa sì che il compilatore generi un metodo finalizzatore che contiene un po 'di codice wrapper insieme a tutto ciò che appare nel corpo del distruttore.
supercat

8

Non ne hai bisogno a meno che la tua classe mantenga risorse non gestite come gli handle di file di Windows.


5
Bene, in realtà, si chiama distruttore
David Heffernan,

2
Ora sono confuso. È finalizzatore o distruttore?

4
La specifica C # infatti la chiama distruttore. Alcuni vedono questo come un errore. stackoverflow.com/questions/1872700/…
Ani

2
@ThomasEding - Sì, lo è . C # usa la sintassi del distruttore, ma in realtà sta creando un finalizzatore .
JDB ricorda ancora Monica il

2
Adoro i commenti qui, vero panto :)
Benjol,

4

Si chiama distruttore / finalizzatore e di solito viene creato durante l'implementazione del modello Disposed.

È una soluzione di fallback quando l'utente della tua classe dimentica di chiamare Dispose, per assicurarsi che (eventualmente) le tue risorse vengano rilasciate, ma non hai alcuna garanzia su quando viene chiamato il distruttore.

In questa domanda Stack Overflow , la risposta accettata mostra correttamente come implementare il modello di disposizione. Ciò è necessario solo se la tua classe contiene risorse non gestite che Garbage Collector non riesce a ripulire.

Una buona pratica è quella di non implementare un finalizzatore senza dare anche all'utente della classe la possibilità di disporre manualmente l'oggetto per liberare immediatamente le risorse.


In realtà NON è chiamato distruttore in C # con buone ragioni.
TomTom

14
In realtà lo è . Grazie per avermi dato un voto negativo perché ti sbagli. Vedere la libreria MSDN relativa a questo specifico problema: msdn.microsoft.com/en-us/library/66x5fx1b.aspx
Øyvind Bråthen

1
@ TomTom il suo nome ufficiale è distruttore
David Heffernan,

In realtà non è un metodo di fallback, consente semplicemente al GC di gestire quando i tuoi oggetti liberano risorse non gestite, l'implementazione di IDisposable ti consente di gestirlo da solo.
HasaniH,

3

Quando hai risorse non gestite e devi assicurarti che vengano ripulite quando il tuo oggetto scompare. Un buon esempio potrebbe essere gli oggetti COM o i gestori di file.


2

Ho usato un distruttore (solo a scopo di debug) per vedere se un oggetto veniva rimosso dalla memoria nell'ambito di un'applicazione WPF. Non ero sicuro che la garbage collection stesse veramente eliminando l'oggetto dalla memoria, e questo era un buon modo per verificarlo.


1

I distruttori forniscono un modo implicito di liberare risorse non gestite incapsulate nella tua classe, vengono chiamate quando il GC vi si avvicina e chiamano implicitamente il metodo Finalize della classe base. Se si utilizzano molte risorse non gestite, è meglio fornire un modo esplicito di liberare tali risorse tramite l'interfaccia IDisposable. Consulta la guida alla programmazione C #: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.