Code review: słowo kluczowe using

W postach oznaczonych “Code review” zamierzam przedstawiam dobre i złe praktyki pisania kodu. Posty przeważnie będą składały się z fragmentu kodu i opisu co jest w nim źle. Dziś spójrzmy na:

StreamReader streamReader = new StreamReader(File.Open(path, FileMode.Open));
string header = streamReader.ReadLine();
string dataPoints = streamReader.ReadLine();
string[] dataPointArray = dataPoints.Split(new char[]{';’});
streamReader.Close();

Co w tym jest jest nie tak? W przypadku gdy np. ReadLine wyrzuci wyjątek, nie będziemy mieli opcji zamknięcia pliku. Dużo lepiej wszelkie operacje IO przechowywać w wyrażeniu using:

using(StreamReader streamReader = new StreamReader(File.Open(path, FileMode.Open)))
{
    string header = streamReader.ReadLine();
    string dataPoints = streamReader.ReadLine();
    string[] dataPointArray = dataPoints.Split(new char[]{';’});
}

14 thoughts on “Code review: słowo kluczowe using”

  1. Dyrektywa dodatkowo pamięta o wywoływaniu Dispose() na rzecz obiektu
    (BTW: nie da się “wsadzić” w using klasy nie obsługującej IDisposable).

    Mam jedno pytanie są jakieś “plusy dodatnie” za utworzenie obiektu StreamReader na podstawie strumienia zwracanego z File.Open(…) niż użyć konstruktor klasy StreamReader(string); gdzie string jest ścieżką do pliku?

  2. @Soltys:
    Using sluzy wlasnie do zwalniania pamieci. Ale dla StreamReader Dispose metoda to po prostu zwykly Close i dlatego nadaje sie tutaj. Jesli z jakich powodow wykonanie wyjdzie poza {} to dokona sie Dispose czyli zamkniecie pliku.

  3. Moim zdaniem “wszystkie” klasy implementujące interfejs IDisposable powinny być wykorzystywane poprzez using. Chyba, że ktoś będzie ręcznie obsługiwał wystąpienie wyjątku, bądź z innych przyczyn chce bawić się w konstrukcję try,catch,finnaly.

  4. Można by tu jeszcze dodać, że zmiennych zdeklarowanych w using nie można przekazywać przez referencję, np:

    using (FileStream stream = new FileStream(@”c:\test.txt”, FileMode.Open))
    {
    Test(ref stream);
    }

    lecz deklarując tę zmienną poza using już można, np:

    FileStream stream = null;
    using (stream = new FileStream(@”c:\test.txt”, FileMode.Open))
    {
    Test(ref stream);
    }

  5. Rozumiem, że using zastępuje:
    try {
    inicjacja obiektu (byłby w “using”)
    ..
    }
    finally
    {
    //close/dispose na obiekcie zainicjowanym (byłby w “using”)
    }

    ale czy zawsze jest to wymagane? Jeśli obiekt jest zarządzalny przez .net to czy GC sam nam go nie zwolni?
    Np obiekt EntitFramework (ObjectContext), czy nie zostanie zwolniony jak trafi w “ręce” GC?

  6. @Damian
    1.Fakt, using kompiluje się mniej więcej do czegoś takiego:

    Foo foo = null;
    try {
    foo = new Foo();
    }
    finally {
    if(foo != null)
    foo.dispose()
    }

    2. Masz rację, ObjectContext jako obiekt zarządzany zostanie zwolniony przez GC jeśli nie będzie do niego referencji w chwili odśmiecania. Owinięcie użycia ObjectContext w blok using to po prostu jawne zażądanie zwolnienie zasobów w chwili opuszczenia tego bloku. Inaczej jest oczywiście z obiektami niezarządzanymi przez CLR.

  7. Z wielokrotnymi blokami using wiąże się też miła składnia, o której sporo osób nie wie, tzn. można napisać:

    using(Foo foo = new Foo())
    using(Foo foo2 = new Foo())
    {

    }

    zamiast

    using(Foo foo = new Foo())
    {
    using(Foo foo2 = new Foo())
    {

    }
    }

  8. Btw. Czy da się użyć usinga tak jak nowej składni try…catch w Javie 7:
    try (Closable closabe = createClosble)
    {
    }
    catch(SomeException specifficEx)
    {
    }
    catch(Exception ex)
    {
    }
    finally
    {
    }
    Bo zastosowanie tej konstrukcji wydaje się być szersze niż usinga

  9. @dooh:
    Dzięki za informacje. Nie będę miał już wątpliwości:)

    @Piotr Zieliński:
    Czekamy na następny wpis;)

  10. @dooh:
    Taka ciekawostka, która powoduje, że to “mniej-więcej” ma wyjątki:

    using(Foo foo = new Foo(1))
    {
    foo = new Foo(2);
    }
    wykona Dispose na obiekcie utworzonym z parametrem 1, natomiast:

    try
    {
    foo = new Foo(1);
    foo = new Foo(2);
    } finally
    {
    foo.Dispose();
    } wykona Dispose na obiekcie utworzonym z parametrem 2

Leave a Reply

Your email address will not be published.