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:

// otwarcie pliku do zapisu i zapisanie tekstu
using (TextWriter tw = new StreamWriter("file.txt"))
{
    tw.Write ("qwertyuiop");
}
// 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 try-finally? 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.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: , ,
Autor: Xion w Programowanie »


Możesz śledzić komentarze do tej notki poprzez kanał RSS 2.0.
Możesz przejść do końca i zostawić komentarz. Śledzenie notek (trackback) jest aktualnie wyłączone.


9 komentarze/y do notki “using w C#”.
  1. moriturius:
    Luty 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:
    Luty 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:
    Luty 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:
    Luty 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:
    Luty 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:
    Luty 16th, 2010 o 22:30

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

  7. Reg:
    Luty 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:
    Luty 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:
    Luty 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.

Dodaj komentarz

Znaki nowej linii dodawane są automatycznie.
Do wstawiania kodu użyj [code][/code], a do wzorów (w LaTeX-u) [tex][/tex].
Dozwolne tagi HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 



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