Jaka jest różnica między destruktorem a metodą Finalize w klasie w języku C #?

97

Jaka jest różnica między destruktorem a metodą Finalize w klasie, jeśli taki istnieje?

Niedawno odkryłem, że program Visual Studio 2008 uważa destruktor za synonim metody Finalize, co oznacza, że ​​program Visual Studio nie pozwala na jednoczesne definiowanie obu metod w klasie.

Na przykład następujący fragment kodu:

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

    public bool Finalize()
    {
        return true;
    }
}

Daje następujący błąd w wywołaniu Finalize w destruktorze:

Wywołanie jest niejednoznaczne między następującymi metodami lub właściwościami: „TestFinalize. ~ TestFinalize ()” i „TestFinalize.Finalize ()”

A jeśli wywołanie Finalize jest zakomentowane, powoduje następujący błąd:

Typ „ManagementConcepts.Service.TestFinalize” już definiuje element członkowski o nazwie „Finalize” z tymi samymi typami parametrów

Jeff Leonard
źródło

Odpowiedzi:

68

Destruktor w C # przesłania System.Object.Finalizemetodę. Państwo musieli użyć składni destructor, aby to zrobić. Ręczne zastąpienie Finalizespowoduje wyświetlenie komunikatu o błędzie.

Zasadniczo to, co próbujesz zrobić z Finalizedeklaracją metody, to ukrycie metody klasy bazowej. Spowoduje to, że kompilator wyświetli ostrzeżenie, które można wyciszyć za pomocą newmodyfikatora (jeśli miał działać). Ważne jest, aby pamiętać, jest to, że ty nie może oba overridei zadeklarować newczłonek z identyczną nazwą w tym samym czasie więc uwzględniając zarówno destruktora i Finalizemetody spowoduje wystąpienie błędu (ale może , choć nie zalecane, zadeklarować public new void Finalize()metodę, jeśli nie deklarujesz destruktora).

Mehrdad Afshari
źródło
71

Wikipedia zawiera dobrą dyskusję na temat różnicy między finalizatorem a destruktorem w artykule o finalizatorze .

C # naprawdę nie ma „prawdziwego” destruktora. Składnia przypomina destruktor C ++, ale tak naprawdę jest finalizatorem. Napisałeś to poprawnie w pierwszej części twojego przykładu:

~ClassName() { }

Powyższe jest cukrem syntaktycznym dla Finalizefunkcji. Zapewnia działanie finalizatorów w bazie, ale poza tym jest identyczne z nadpisywaniem Finalizefunkcji. Oznacza to, że kiedy piszesz składnię destruktora, tak naprawdę piszesz finalizator.

Według Microsoftu , finalizator odnosi się do funkcji wywoływanej przez garbage collectora, kiedy zbiera ( Finalize), podczas gdy destruktor to twój bit kodu, który jest wykonywany jako wynik (cukier składniowy, który się staje Finalize). Są tak blisko, że są tym samym, że Microsoft nigdy nie powinien był tego rozróżniać.

Użycie przez Microsoft terminu „destructor” w C ++ jest mylące, ponieważ w C ++ jest ono wykonywane w tym samym wątku, gdy tylko obiekt zostanie usunięty lub wyjęty ze stosu, podczas gdy w C # jest on wykonywany w oddzielnym wątku w innym czasie.

Kenzi
źródło
Twierdziłbym, że takie rozróżnienie między destruktorem a finalizatorem jest ważne. Jednak tylko tym, którym zależy na tym, co dzieje się pod maską, będzie zależało na tym wyróżnieniu.
Kyle Baran
1
Należy również zauważyć, że ECMA-334 już dawno formalnie wyraźnie ujednoznacznił „destruktor” i „finalizator”. Nie wiem, dlaczego stwardnienie rozsiane wciąż nalega na wprowadzający w błąd termin w ich specyfikacjach.
FrankHB
Przynajmniej podczas pracy z Mono, C # jest faktycznie wzorowany na C ++, a większość natywnych obiektów C # to obiekty C ++. Sposób działania kompilatora, który skompilował Mono, dyktuje, w jaki sposób te obiekty C ++ są niszczone, a także w jaki sposób finalizacja obiektu C # propaguje się do C ++ i wywołuje te destruktory. To rozróżnienie ma sens pod maską, ale nadal nie ma zastosowania do samego C #.
Kenzi
20

Znaleziono tutaj: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Burzyciel

    Są to specjalne metody zawierające oczyszczający kod dla obiektu. Nie możesz wywołać ich jawnie w swoim kodzie, ponieważ są wywoływane niejawnie przez GC. W języku C # mają taką samą nazwę jak nazwa klasy poprzedzona ~znakiem. Lubić-

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

    W VB.NET destruktory są implementowane poprzez nadpisanie metody Finalize klasy System.Object.

  2. Dysponować

    Są one tak samo jak wszystkie inne metody w klasie i można je wywołać jawnie, ale mają specjalny cel czyszczenia obiektu. W metodzie dispose piszemy kod porządkujący dla obiektu. Ważne jest, abyśmy zwolnili wszystkie niezarządzane zasoby w metodzie dispose, takie jak połączenie z bazą danych, pliki itp. Klasa implementująca metodę dispose powinna implementować interfejs IDisposable. Metoda Dispose powinna wywołać metodę GC.SuppressFinalize dla obiektu, który usuwa, jeśli class ma desturctor, ponieważ wykonał już pracę w celu wyczyszczenia obiektu, więc nie jest konieczne, aby moduł odśmiecania pamięci wywoływał metodę Finalize obiektu. Źródła: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Sfinalizować

    Metoda Finalize działa jako zabezpieczenie w celu oczyszczenia zasobów w przypadku, gdy metoda Dispose nie zostanie wywołana. Aby oczyścić niezarządzane zasoby, należy zaimplementować tylko metodę Finalize. Nie należy implementować metody Finalize dla obiektów zarządzanych, ponieważ moduł wyrzucania elementów bezużytecznych automatycznie czyści zarządzane zasoby. Metoda Finalize jest wywoływana przez GC niejawnie, dlatego nie można jej wywołać z kodu.

    Uwaga: W języku C # metoda Finalize nie może być nadpisana, więc musisz użyć destruktora, którego wewnętrzna implementacja zastąpi metodę Finalize w MSIL, ale w VB.NET metodę Finalize można przesłonić, ponieważ obsługuje metodę destructor.

Aktualizacja: Ciekawy wątek częściowo powiązany .

Andrew Siemer
źródło
1
You should only implement a Finalize method to clean up unmanaged resources: umieściłeś to w Finalize. To samo z Dispose?
hqt
@hqt: Przypadki, w których należy wdrożyć Disposeznacznie więcej niż te, w których należy zaimplementować finalizator. Zaimplementuj, Disposejeśli jest prawdopodobne, że instancja klasy lub klasy pochodnej będzie ostatnią rzeczą, która będzie bezpośrednio posiadać niezarządzany zasób lub bezpośrednio posiadasz ostatnią rzecz, aby bezpośrednio posiadać niezarządzany zasób lub bezpośrednio posiadać ostatnią rzecz, która ma być bezpośrednio właścicielem itd. Stosuj tylko Finalizedo czyszczenia zasobów, jeśli klasa <i> bezpośrednio </i> posiada niezarządzany zasób <i> i prawie nic więcej </i> - znacznie węższy scenariusz.
supercat
@hqt: Jeśli klasa bezpośrednio posiadałaby niezarządzane zasoby, a także zawierałaby odniesienia do innych obiektów, niezarządzane zasoby powinny być generalnie podzielone na własną sfinalizowaną klasę (która w idealnym przypadku nie powinna zawierać żadnych silnych odniesień do niczego innego), co oznacza klasę który zawiera odniesienia do innych obiektów, posiadałby jedynie „rzeczy, które bezpośrednio posiadają niezarządzane zasoby”, zamiast posiadać same zasoby, a zatem nie potrzebowałby finalizatora.
supercat