2009-09-10 19 views
6

Czytałem artykuł z dnia this i zastanawiałem się, dlaczego był Finalizer wraz z metodą Dispose. Czytam here na SO, dlaczego warto dodać Dispose do Finalizera. Ciekaw jestem, skąd Finalizator miałby nazywać samą metodę usuwania? Czy istnieje przykład kodu lub czy jest on oparty na czymś, co dzieje się w systemie, w którym działa oprogramowanie? Jeśli tak, to co może się zdarzyć, że nie ma metody usuwania uruchamianej przez GC.Kiedy metoda dispused nie zostanie wywołana?

Odpowiedz

9

Celem finalizatora jest tutaj po prostu zabezpieczenie przed wyciekami pamięci (jeśli zdarzy ci się , a nie, aby jawnie wywołać Dispose). Oznacza to również, że nie musisz wyrzucać swoich obiektów, jeśli chcesz, aby zwolniły zasoby po zamknięciu programu, ponieważ GC będzie zmuszony do sfinalizowania i zebrania wszystkich obiektów.

W związku z tym ważne jest, aby pozbyć się obiektu nieco inaczej, robiąc to z finalizatora.

~MyClass() 
{ 
    Dispose(false); 
} 

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

protected void Dispose(disposing) 
{ 
    if (!this.disposed) 
    { 
     if (disposing) 
     { 
      // Dispose managed resources here. 
     } 
     // Dispose unmanaged resources here. 
    } 
    this.disposed = true; 
} 

Powodem ty nie chcą wyrzucać zarządzanych zasobów w finaliser jest to, że rzeczywiście można tworzyć silne odniesienia do nich w ten sposób, a to mogłoby zapobiec GC ze robi to zadanie prawidłowo i gromadzenie im. Zasoby niezarządzane (np. Win32 i inne) powinny zawsze być jawnie zamknięte/usuwane, oczywiście, ponieważ CLR nie ma wiedzy na ich temat.

+1

I kolejny powód, dla którego nie należy dysponować zarządzanymi zasobami w twoim finalizatorze ... Możliwe, że zostały już pobrane przez GC do czasu wykonania twojego finalizatora. Próba ich usunięcia, gdy zostały już zebrane, spowoduje błąd runtime. – LukeH

+1

@Luke: To prawda, ale można tego łatwo uniknąć, ustawiając wszystkie odniesienia na wartość null, a następnie wykonując zerową kontrolę przed pozbyciem się. – Noldorin

+0

@Noldorin - Gdzie w twoim przykładzie miałoby się wyrejestrować wydarzenia? Rozumiem, że z technologicznego punktu widzenia są oni objęci zarządzaniem, ale co by było, gdybyśmy mieli jakiś obiekt związany z tą klasą przez zdarzenie i nie wyrejestrowaliśmy go w części niezarządzanej (zakładając, że użytkownik nie zadzwoni bezpośrednio i po lewej stronie do GC, aby go wyczyścić w górę). Czy byłoby dobrze/bezpiecznie umieścić wyrejestrowywanie zdarzeń w zarządzanej sekcji, aby upewnić się, że tak się dzieje? Efektem ubocznym może być ktoś, kto myśli, że pozbywa się obiektu, ale tak naprawdę nigdy nie zostaje usunięty z powodu powiązania zdarzenia między tą klasą a drugą. – SwDevMan81

4

Ma to głównie na celu ochronę. Nie możesz dyktować, co zrobi końcowy użytkownik twojej klasy. Zapewniając finalizator oprócz metody usuwania, GC "usunie" twój obiekt, odpowiednio uwalniając twoje zasoby, nawet jeśli użytkownik zapomni zadzwonić do Dispose() lub niewłaściwie używa twojej klasy.

+1

Warto nadmienić, że GC jest niedeterministyczny, więc nie ma gwarancji, że kiedy, * lub nawet *, twój finalizator zostanie wywołany. – LukeH

+0

Tak - Jeśli twój program działa wystarczająco długo, twój obiekt najprawdopodobniej zostanie sfinalizowany. Ponadto, jeśli wszystko wyłączy się całkowicie, zostanie sfinalizowane. Ale nie ma gwarancji z GC - co jest jednym z powodów, dla których IDisposable istnieje w pierwszej kolejności. –

1

Metodę zbycia należy wyraźnie wywołać, wywołując metodę Dispose() lub umieszczając obiekt w instrukcji using. GC będzie zawsze wywoływał finalizator, więc jeśli coś musi się wydarzyć zanim przedmioty zostaną usunięte, finalizator powinien przynajmniej sprawdzić, czy wszystko w obiekcie jest oczyszczone.

Chcesz uniknąć czyszczenia obiektów w finalizatorze, jeśli w ogóle jest to możliwe, ponieważ powoduje to dodatkową pracę w porównaniu do ich wyrzucania przed rozdaniem (np. Wywoływanie zrzucania), ale zawsze powinieneś przynajmniej sprawdzić finalizator, jeśli są jakieś obiekty leżąc wokół, które trzeba usunąć.

2

Finalizer jest wywoływany, gdy obiekt jest zbędny. Dispose należy jawnie wywoływać. W poniższym kodzie finalizator zostanie wywołany, ale metoda Dispose nie jest.

class Foo : IDisposable 
{ 
    public void Dispose() 
    { 
    Console.WriteLine("Disposed"); 
    } 

    ~Foo() 
    { 
    Console.WriteLine("Finalized"); 
    } 
} 

... 

public void Go() 
{ 
    Foo foo = new Foo(); 
} 
+1

To nie do końca prawda. Finalizator jest wywoływany jakiś czas po tym, jak obiekt w przeciwnym razie byłby uprawniony do zbierania śmieci (tzn. Aplikacja nie odwołuje się już do instancji). Jednakże, ponieważ finalizator musi być uruchamiany dla instancji, CLR w rzeczywistości powoduje zakorzenienie obiektu, a zatem nie jest zbiorem śmieci, dopóki nie uruchomi się finalizator. –

+0

Nie ma również gwarancji, że obiekt będzie * kiedykolwiek * był GC'd lub że jego finalizator * kiedykolwiek * zostanie wywołany. Dlatego jest podwójnie ważne, aby zapewnić prawidłowe usuwanie obiektów "IDisposable". – LukeH

0

Ważnym ale subtelna nuta jeszcze wymienić: a rzadko uważane Zużyty celem jest zapobieganie obiektu z czyszczonej się przedwcześnie. Obiekty z finalizatorami muszą być napisane starannie, aby finalizator nie działał inaczej niż oczekiwano. Finalizer nie może zostać uruchomiony przed rozpoczęciem ostatniego wywołania metody, które zostanie wykonane na obiekcie (*), ale może czasami uruchomić ostatnie wywołanie metody, jeśli obiekt zostanie opuszczony po zakończeniu metody. Kod, który prawidłowo Pozbywa się obiektu, nie może opuścić obiektu przed wywołaniem Dispose, więc nie ma niebezpieczeństwa, że ​​finalizator sieje spustoszenie w kodzie, który właściwie używa Dispose. Z drugiej strony, jeśli ostatnia metoda użycia obiektu korzysta z obiektów, które zostaną oczyszczone w finalizatorze po ostatnim użyciu odwołania do obiektu, możliwe jest wywołanie przez moduł zbierający śmieci finalizacji na obiekcie i wyczyszczenie go. do jednostek, które są nadal w użyciu.Rozwiązaniem jest zapewnienie, że każda metoda wywołania, która używa jednostek, które mają zostać oczyszczone przez finalizatora, musi być w pewnym momencie śledzona przez wywołanie metody, które używa "tego". GC.KeepAlive (this) jest dobrą metodą do tego celu.

(*) Metody inne niż wirtualne, które są rozszerzone na kod w linii, który nie robi nic z tym obiektem, mogą być wyłączone z tej reguły, ale utylizacja zwykle jest lub wywołuje metodę wirtualną.

Powiązane problemy