Implementacja IDisposable

0

Witam
Chciałbym się dowiedzieć więcej na temat zastosowania implementacji IDisposable. Oczywiście wiem, że powinno zwalniać za jego pomocą zasoby niezarządzalne tak by nie powstawały wycieki pamięci. Co w sytuacji kiedy mamy klasę bez takich zasobów. Powiedzmy, że mam klasę, która przetwarza jakieś wyniki tylko raz w trakcie działania aplikacji i chce się pozbyć tego obiektu z pamięci - nie chce wykorzystywać klasy statycznej, a chciałbym wykorzystać klauzulę using. Zatem czy taka konstrukcja będzie poprawna ?

public class SomeClass : IDisposable
{
  // reszta metod
  public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
}
 
2

Nie w ogólności, a w szczególności - bardzo nie.

  • w ogólności, bo wywołania GC są niezalecane i są przez GC traktowane tylko jako sugestia posprzątania. GC może, ale nie musi posprzątać po obiekcie. Na pewno wywołanie GC w Dispose nie sprzątnie obiektu, bo przecież kod obiektu się aktualnie wykonuje - wywołał GC - więc nie kwalifikuje się do usunięcia.
  • w szczególności, bo to, co wywołałeś informuje GC, że ma nie wyrzucać obiektu z pamięci, a zdaje się, że chciałeś osiągnąć coś przeciwnego? Sama nazwa metody zresztą mówi - zablokuj "finalizację" obiektu.

Skoro obiekt nie ma nic do sprzątania, to nie powinien implementować IDisposable. W praktyce jedyny przypadek, kiedy tego potrzebujesz występuje w momencie, kiedy klasa opakowuje inną klasę implementującą ten interfejs.

1

Nazwa IDisposable jest trochę myląca, ponieważ sugeruje zwolnienie obiektu z pamięci. Tymczasem zastosowań tego interfejsu jest wiele. Wystarczy spojrzeć w definicje klas .NET żeby przekonać się, że naprawdę gros z nich implementuje ten właśnie interfejs.

Samo zastosowanie IDisposable to po prostu najczęściej użycie using, a tym samym wywołanie try { } finally { } podczas tworzenia obiektu klasy implementującej ten interfejs. Dzięki temu ma się pewność, że blok finally zawsze zostanie wykonany. Do czego to jest używane?

  • Connectory do baz danych - zamykają połączenie automatycznie, gdyż w swojej metodzie Dispose posiadają instrukcje zakończenia połączenia. Programista nie musi pamiętać o wywołaniu Close();
  • Narzędzia kryptograficzne - zamykanie kryptostrumieni czy zwalnianie obiektów odpowiedzialnych za szyfrowanie;
  • Strumienie wszelkiej maści - w sumie to co wyżej;

Nic w tym interfejsie odkrywczego nie ma. Ot bardzo przydatna rzecz, która oszczędza pisania, bo starczy utworzyć obiekt takiej klasy w using i masz zawsze pewne wywołanie jego metody Dispose. Tyle.

0

Dobrze, ale jak w programie mam klasę właśnie przetwarzającej dane i chce się jej pozbyć z pamięci to co wówczas zrobić. Chciałbym wykorzystywać klauzulę using, ponieważ jest to wygodne.

1

C# to nie C/C++. Nie da się tego zrobić w deterministyczny sposób, ponieważ tutaj działa to inaczej. GC dba o zwalnianie zasobów i obiekt zostaje przez niego zwolniony kiedy w programie nie będzie istniała do niego żadna referencja. To tak najprościej. Jeżeli w C# chcesz zwalniać z pamięci obiekty ręcznie to znaczy, że coś źle robisz. Wyjątkiem od tej reguły są obiekty, którymi nie może zająć się GC gdyż są niezarządzalne ale Twój obiekt na pewno taki nie jest.

Możesz wywołać:

GC.Collect();
GC.WaitForPendingFinalizers();

Ale to jeszcze nie znaczy, że obiekt zostanie zwolniony wtedy kiedy tego chcesz.

Chciałbym wykorzystywać klauzulę using, ponieważ jest to wygodne.

Wszystkie zależy od tego jak klasa przetwarza dane. Jeżeli korzysta z jakichś strumieni to w metodzie Dispose po prostu je pozamykaj i tyle w temacie. Zwalnianiem samego obiektu zajmie się GC w swoim czasie.

Dobrze, ale jak w programie mam klasę właśnie przetwarzającej dane i chce się jej pozbyć z pamięci to co wówczas zrobić

Najprościej ustaw referencje do niej na null :) z tym, że aktualne jest oczywiście wszystko co napisałem w tym poście i wszystko to co opisał Ci @ŁF

1
grzesiek51114 napisał(a):

Samo IDisposable to po prostu wywołanie try { } finally { }. Dzięki temu ma się pewność, że blok finally zawsze zostanie wykonany.

IDisposable nie ma nic wspólnego z try/finally. Masz na myśli using? To też dość odległy skrót myślowy.

@autor - jeśli obiekt, który chcesz zwolnić, implementuje IDisposable, to sposób wywołania metody Dispose() można zapewnić na trzy sposoby, zależnie od tego jak używasz obiektu. Jeśli obiekt jest zmienną lokalną, to najłatwiej będzie go stworzyć w klauzuli using. Możesz też zrobić MyClass myClass = null; try { myClass = new MyClass(); ... } finally { myClass?.Dispose(); } - ale o ile więcej pisania jest. Jeśli zaś obiekt jest polem/właściwością obiektu, to zwolnisz go w swojej metodzie Dispose() implementując w swojej klasie IDisposable. Na odwrót się nie da.

1 użytkowników online, w tym zalogowanych: 0, gości: 1