In C # qual è la differenza tra un distruttore e un metodo Finalize in una classe?


97

Qual è la differenza, se presente, tra un distruttore e un metodo Finalize in una classe?

Recentemente ho scoperto che Visual Studio 2008 considera un distruttore sinonimo di un metodo Finalize, il che significa che Visual Studio non ti consente di definire contemporaneamente entrambi i metodi in una classe.

Ad esempio, il seguente frammento di codice:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Fornisce il seguente errore sulla chiamata a Finalize nel distruttore:

La chiamata è ambigua tra i seguenti metodi o proprietà: "TestFinalize. ~ TestFinalize ()" e "TestFinalize.Finalize ()"

E se la chiamata a Finalize è commentata, restituisce il seguente errore:

Il tipo "ManagementConcepts.Service.TestFinalize" definisce già un membro denominato "Finalize" con gli stessi tipi di parametro

Risposte:


68

Un distruttore in C # sovrascrive il System.Object.Finalizemetodo. È necessario utilizzare la sintassi distruttore di farlo. Ignorare manualmenteFinalize ti darà un messaggio di errore.

Fondamentalmente quello che stai cercando di fare con la Finalizedichiarazione del tuo metodo è nascondere il metodo della classe base. Farà sì che il compilatore emetta un avviso che può essere tacitato usando il newmodificatore (se avrebbe funzionato). La cosa importante da notare qui è che non può entrambi overridee dichiarare un newmembro con nome identico allo stesso tempo in modo da avere sia un distruttore ed un Finalizemetodo si tradurrà in un errore (ma è possibile , anche se non è raccomandato, dichiarare un public new void Finalize()metodo se non stai dichiarando un distruttore).


71

Wikipedia ha alcune buone discussioni sulla differenza tra un finalizzatore e un distruttore nell'articolo del finalizzatore .

C # non ha davvero un distruttore "vero". La sintassi assomiglia a un distruttore C ++, ma in realtà è un finalizzatore. Lo hai scritto correttamente nella prima parte del tuo esempio:

~ClassName() { }

Quanto sopra è zucchero sintattico per una Finalizefunzione. Assicura che i finalizzatori nella base siano garantiti per l'esecuzione, ma per il resto è identico all'override della Finalizefunzione. Ciò significa che quando scrivi la sintassi del distruttore, stai davvero scrivendo il finalizzatore.

Secondo Microsoft , il finalizzatore si riferisce alla funzione che il garbage collector chiama quando raccoglie ( Finalize), mentre il distruttore è il tuo bit di codice che viene eseguito come risultato (lo zucchero sintattico che diventaFinalize ). Sono così vicini all'essere la stessa cosa che Microsoft non avrebbe mai dovuto fare la distinzione.

L'uso da parte di Microsoft del termine "distruttore" del C ++ è fuorviante, perché in C ++ viene eseguito sullo stesso thread non appena l'oggetto viene eliminato o estratto dallo stack, mentre in C # viene eseguito su un thread separato in un altro momento.


Direi che una tale distinzione tra il distruttore e il finalizzatore è importante da fare. Tuttavia, solo quelli che si preoccupano di quello che sta succedendo sotto il cofano si preoccuperebbero di detta distinzione.
Kyle Baran

1
Si noti inoltre che ECMA-334 ha disambiguato formalmente "distruttore" e "finalizzatore", molto tempo fa. Non so perché MS insista ancora sul termine fuorviante nelle loro specifiche.
FrankHB

Almeno lavorando con Mono, C # è effettivamente modellato su C ++ e la maggior parte degli oggetti C # nativi sono oggetti C ++. Il modo in cui funziona il compilatore che ha compilato Mono determina il modo in cui gli oggetti C ++ vengono distrutti e, allo stesso modo, come la finalizzazione degli oggetti C # si propaga fino a C ++ e chiama quei distruttori. La distinzione ha senso sotto il cofano, ma non si applica ancora a C # stesso.
Kenzi

20

Trovato qui: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Distruttore

    Sono metodi speciali che contengono il codice di pulizia per l'oggetto. Non è possibile chiamarli esplicitamente nel codice poiché vengono chiamati implicitamente da GC. In C # hanno lo stesso nome del nome della classe preceduto dal ~segno. Piace-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }

    In VB.NET, i distruttori vengono implementati sovrascrivendo il metodo Finalize della classe System.Object.

  2. Smaltire

    Questi sono proprio come qualsiasi altro metodo nella classe e possono essere chiamati esplicitamente ma hanno uno scopo speciale di ripulire l'oggetto. Nel metodo dispose scriviamo il codice di ripulitura per l'oggetto. È importante che abbiamo liberato tutte le risorse non gestite nel metodo dispose come la connessione al database, i file, ecc. La classe che implementa il metodo dispose dovrebbe implementare l'interfaccia IDisposable. Un metodo Dispose dovrebbe chiamare il metodo GC.SuppressFinalize per l'oggetto che sta eliminando se il class ha un desturctor perché ha già fatto il lavoro per ripulire l'oggetto, quindi non è necessario che il garbage collector chiami il metodo Finalize dell'oggetto. Riferimento: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finalizza

    Un metodo Finalize funge da protezione per ripulire le risorse nel caso in cui il metodo Dispose non venga chiamato. È necessario implementare solo un metodo Finalize per pulire le risorse non gestite. Non è necessario implementare un metodo Finalize per gli oggetti gestiti, poiché il Garbage Collector pulisce automaticamente le risorse gestite. Il metodo Finalize viene chiamato implicitamente dal GC quindi non è possibile chiamarlo dal codice.

    Nota: in C #, il metodo Finalize non può essere sovrascritto, quindi è necessario utilizzare il distruttore la cui implementazione interna sovrascriverà il metodo Finalize in MSIL.Ma in VB.NET, il metodo Finalize può essere sovrascritto perché supporta il metodo distruttore.

Aggiornamento: interessante thread semi-correlato qui .


1
You should only implement a Finalize method to clean up unmanaged resources: lo metti in Finalize. Lo stesso con Dispose?
hqt

@hqt: i casi in cui si dovrebbe implementare sono di Disposegran lunga superiori a quelli in cui si dovrebbe implementare un finalizzatore. Implementare Disposese è probabile che un'istanza della classe o una classe derivata sarà l'ultima cosa a possedere direttamente una risorsa non gestita, o possedere direttamente l'ultima cosa per possedere direttamente una risorsa non gestita, o possedere direttamente l'ultima cosa di cui possedere direttamente ecc. Eseguire l'implementazione Finalizeper la pulizia delle risorse solo se la propria classe <i> direttamente </i> possiede una risorsa non gestita <i> e quasi nient'altro </i>, uno scenario molto più ristretto.
supercat

@hqt: Se una classe possiede direttamente risorse non gestite e contiene anche riferimenti ad altri oggetti, le risorse non gestite dovrebbero generalmente essere suddivise nella propria classe finalizzabile (che idealmente non dovrebbe contenere riferimenti forti a nient'altro), il che significa che la classe che contiene riferimenti ad altri oggetti possederebbe solo "cose ​​che possiedono direttamente risorse non gestite", piuttosto che possedere le risorse stesse, e quindi non avrebbe bisogno di un finalizzatore.
supercat
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.