using w C#

2010-02-15 17:01

W C++ nie ma mechanizmu typu garbage collector, więc jedyne automatyczne zwalnianie obiektów, jakie w tym języku występuje, dotyczy tych lokalnych – tworzonych na stosie. Dlatego wszelkiego typu pomocnicze obiekty (np. uchwyty do zewnętrznych zasobów, jak pliki) deklaruje się tu zwykle jako właśnie zmienne lokalne.
W innych językach z kolei – dokładniej: w tych, w których GC występuje – praktycznie wszystkie obiekty są tworzone na stercie i zarządzane przez odśmiecacz pamięci. Nie musimy więc martwić się o to, jak i kiedy zostaną one zwolnione.

Ta zaleta staje się jednak wadą w sytuacji, gdy chcielibyśmy jednak móc swoje obiekty niszczyć samodzielnie. Jeśli na przykład mamy do czynienia ze wspominanym już uchwytem do zewnętrznego zasobu, to pewnie życzylibyśmy sobie, by został on zamknięty jednak nieco wcześniej niż na końcu działania programu (w niezbyt dużych aplikacjach zazwyczaj dopiero wtedy włącza się garbage collector). Inaczej będziemy niepotrzebnie zjadać zasoby systemowe.
W C# najlepszym sposobem na ograniczenie czasu życia obiektu jest instrukcja using (w tym kontekście to słowo kluczowe nie znaczy wcale użycia przestrzeni nazw!). Podajemy jej po prostu obiekt, którego chcemy użyć wewnątrz bloku; w zamian mamy zapewnione, że związane z nim zasoby zostaną zwolnione po wyjściu z tego bloku. Prosty przykład wygląda choćby tak:

  1. // otwarcie pliku do zapisu i zapisanie tekstu
  2. using (TextWriter tw = new StreamWriter("file.txt"))
  3. {
  4.     tw.Write ("qwertyuiop");
  5. }
  6. // tutaj plik jest już zamknięty

Czemu jednak samodzielnie nie wywołać tego Close czy innej podobnej metody, która służy do zwolnienia zasobu?… Ano choćby dlatego, że istnieje coś takiego jak wyjątki. O ile powoływanie się na ten fakt w C++ bywa zwykle nadmiarem ostrożności, o tyle w .NET wyjątki latają praktycznie stale i mogą być rzucane przez właściwie każdą instrukcję. Nie można więc pomijać możliwości ich wystąpienia i liczyć na to, że mimo niedbałego kodowania wyciek zasobów jakimś cudem nigdy nam się nie trafi.
Może więc lepiej użyć zwykłego bloku tryfinally? Zasadniczo using jest mu równoważny, a ponadto ma jeszcze dodatkowe zalety: automatycznie sprawdza istnienie obiektu przez jego zwolnieniem i ogranicza zasięg zmiennej przechowującej do niego referencję (jeśli deklarujemy ją tak, jak powyżej). Ponadto pozwala też nie wnikać w to, jaką metodę – Close, Disconnect, Release, End, … – trzeba by wywołać na koniec w bloku finally. Jako że wymagane jest, by obiekt w using implementował interfejs IDisposable, będzie to zawsze metoda Dispose, która zawsze posprząta i pozamyka wszystko co trzeba.

Tags: , ,
Author: Xion, posted under Programming »


9 comments for post “using w C#”.
  1. moriturius:
    February 16th, 2010 o 7:06

    To fajne!
    W Javie niestety nie spotkałem się z czymś takim, a to bardzo przydatne. Można co prawda ręcznie wywołać garbage collector, ale to też nie daje do końca pewności, że wszystko zostanie usunięte…
    W Majkrosofcie postarali się aby się fajnie i łatwo kodziło :)

  2. TeWu:
    February 16th, 2010 o 10:40

    W javie zastosowano po prostu inną filozofie:
    “Nikt nie będzie chciał wszystko usuwać i marnować na to moc obliczeniową i czas potrzebny na odwołania do pamięci, takie czyszczenie będzie wykonywane wtedy kiedy trzeba, jak najmniejszym kosztem”
    A jeżeli chodzi o zamknięcie pliku, to można to zrobić zawsze wywołując metodę close().

    Co do składni using, to fajnie jest móc traktować } jako wywołanie metody, w javie nigdy takie coś pewnie nie będzie możliwe :) Z drugiej strony, na przykładzie c++, ile problemów miałem (i nie ja jedyny) podczas tworzenia obiektów na stosie które potem na } wywoływały destruktor i przez to zwalniały coś czego wcale nie chciałem zwalniać i powodowały problemy w zupełnie innym miejscu kodu :) Tak więc zbytnie zwiększenie znaczenia } nie jest też do końca dobrą rzeczą, jeżeli chodzi o czytelność kodu.

  3. Łukasz Kurzyniec:
    February 16th, 2010 o 12:00

    Nie zapominajmy o tym, że zasadniczą przewagą ‘try’ nad ‘using’ jest to, iż ‘try’ wyłapie nam wyjątek, ‘using’ już nie, np: gdy nie mamy uprawnień do zapisu.

  4. Crane:
    February 16th, 2010 o 16:39

    Ale w Javie 7 to się pojawi xD http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000011.html

  5. Xion:
    February 16th, 2010 o 19:12

    @TeWu: “Z drugiej strony, na przykładzie c++, ile problemów miałem (i nie ja jedyny) podczas tworzenia obiektów na stosie które potem na } wywoływały destruktor i przez to zwalniały coś czego wcale nie chciałem zwalniać i powodowały problemy w zupełnie innym miejscu kodu :)”
    W takim razie po prostu niepotrzebnie tworzyłeś te obiekty na stosie. Może to przyzwyczajenie z innych języków, gdzie zmienna typu X, gdzie X jest klasą, to tak naprawdę tylko referencja do obiektu tej klasy.

    @ŁK: try-catch(-finally) wyłapie, samo try-finally nie. using jest odpowiednikiem tego drugiego.

    @Crane: No i bardzo dobrze – obok stringów w switch to będzie druga epokowa zmiana jakościowa na plus w Javie 7 ;-)

  6. nilphilus:
    February 16th, 2010 o 22:30

    jak ktoś jest uparty to wskaźników może użyć ;-)

  7. Reg:
    February 21st, 2010 o 12:28

    Ta konstrukcja using z C# to moim zdaniem genialny wynalazek. Warto dodać, kiedy należy go stosować:
    – Do zasobów będących nakładkami na natywne, których nie powinno się gromadzić w pamięci coraz więcej – jak zasoby GDI do rysowania typu pędzle, bitmapy.
    – Do plików, żeby nie pozostawały otwarte kiedy kod skończy ich używać.

  8. Fuzzy:
    February 21st, 2010 o 20:38

    @Xion

    No nie do końca try-finally to jest to samo co using, ponieważ wyjątek rzucony w bloku using “wyleci” z niego i “poleci” dalej, a finally go “zatrzymuje”.

    Poza tym druga różnica: w bloku using, jego zmienna nie może być użyta jako argument przekazywany przez referencję. W bolku try-catch może być.

    ale z drugiej strony – wiem o co Ci chodziło.. czepiam się słówek ;)

  9. Xion:
    February 21st, 2010 o 20:55

    @Fuzzy

    No nie do końca try-finally to jest to samo co using, ponieważ wyjątek rzucony w bloku using “wyleci” z niego i “poleci” dalej, a finally go “zatrzymuje”.

    Bullshit – finally nic nie zatrzymuje. W finally jest kod wykonywany zawsze po opuszczeniu try – niezależnie od tego, jak (wyjątkiem lub normalnie). Wyjątki zatrzymuje tylko catch, jak sama nazwa wskazuje.

    Poza tym druga różnica: w bloku using, jego zmienna nie może być użyta jako argument przekazywany przez referencję. W bolku try-catch może być.

    W bloku try-catch nie masz żadnej wyróżnionej zmiennej, więc to dziwny argument.

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.