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[]{';’}); }
podoba mi sie ta seria
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?
Krótko i na temat. Chociaż przykład z transakcją SQL byłby chyba lepszy.
@Soltys:
Nie, moze z wyjatkiem FileMode (Open,OpenCreate itp.)
@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.
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.
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);
}
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?
@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.
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())
{
…
}
}
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
@dooh
można nawet tak:
using (Font font3 = new Font(“Arial”, 10.0f),
font4 = new Font(“Arial”, 10.0f))
{
// Use font3 and font4.
}
@dooh:
Dzięki za informacje. Nie będę miał już wątpliwości:)
@Piotr Zieliński:
Czekamy na następny wpis;)
@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