Правильная утилизация файловых потоков и двоичных потоков и утилизация файловых потоков
На самом деле, я попытался защитить свой код от ошибок и в итоге он выглядел довольно грязно.
У меня есть функция, настроенная для чтения файлов определенного типа. Я хочу, чтобы функция возвращала false, если возникла проблема, или true, если все работало. У меня проблемы с выяснением, как все структурировать.
У меня есть начальный блок try-catch, который пытается открыть поток файлов. После этого, однако, у меня есть некоторые другие проверки, которые я делаю в процессе чтения, такие как размер файла и значения при определенных смещениях. Я настроил это с помощью операторов if else. Такие как:
if(condition){
}
else{
MessageBox.Show("There was an error");
br.Dispose();
fs.Dispose();
return false;
}
... br - двоичный читатель и фс файловый поток. Подобных блоков много, и кажется плохой практикой писать одно и то же много раз. Первое, что приходит на ум, - это обернуть все это в оператор try-catch и генерировать исключения вместо использования блоков if else. Я помню, когда читал об операторах try-catch, что хорошо иметь их, но не оборачивать ими все. Честно говоря, я до сих пор не до конца понимаю, почему было бы плохой практикой оборачивать все в операторы try catch, поскольку они дают эффект только при возникновении ошибки, и в этом случае программа все равно идет на юг...
Кроме того, я должен закрыть двоичный читатель и поток файла, или закроет одно закрытие другого? Есть ли способ использовать их, не избавляясь от них?
5 ответов
Как насчет использования using
ключевое слово? это оборачивает ваше использование IDisposable
в попытке - наконец блокировать;
bool success = true;
using(var fs = new FileStream(fileName, FileMode.Create)))
using(var br = new BinaryReader(fs))
{
// do something
success = result;
}
return success;
Вложенные блоки использования гарантируют, что как файловый поток, так и двоичный читатель всегда правильно закрыты и расположены.
Вы можете прочитать больше об использовании в MSDN. Это делает использование IDisposable
немного аккуратнее, избавляя от необходимости явной обработки исключений.
Что касается вашего заявления:
Я помню, когда читал об операторах try-catch, что хорошо иметь их, но не оборачивать ими все.
Я всегда использую простое правило: если я не могу обработать и восстановить исключение в определенном блоке кода, не пытайтесь его перехватить. Разрешить исключению "накапливать" стек до точки, где имеет смысл его перехватить. При таком подходе вы обнаружите, что вам не нужно добавлять много блоков try-catch, вы будете склонны использовать их в момент интеграции со службами (такими как файловая система, сеть и т. Д.), Но ваша бизнес-логика почти всегда без механизмов обработки исключений.
Просто используйте using
Ключевое слово для ваших одноразовых предметов. В пределах блока using
ключевое слово вы можете throw
исключения или return
без необходимости беспокоиться об утилизации; это произойдет автоматически для вас.
try-catch
блоки не очень хорошая идея только потому, что существует гораздо лучшая альтернатива: try-finally
блоки. Но using
Ключевое слово еще лучше, потому что оно существенно расширяется в try-finally
блок и он заботится об утилизации объекта.
Закрытие файлового потока также закроет двоичное считывающее устройство, как и удаление их. Почему вы хотите использовать их без утилизации? Утилизировать их лучше, а утилизировать их через using
это лучшее, что можно сделать.
Да, это плохая практика.
Вместо того, чтобы возвращать логические значения, которые указывают, возникла проблема или нет, вы должны выбросить исключения. Пример:
if (headNotValid)
throw new Exception("Header was not valid");
В некоторых случаях может быть целесообразно создать новый класс исключений.
При работе с классами, которые наследуются от IDisposable
Вы должны использовать using
директивы.
using (var stream = new FileStream(filename))
{
}
Это гарантирует, что ваш поток будет удален, даже если в пределах using
блок.
В целом, я бы предпочел что-то вроде этого:
private void string ParseFile(string filename)
{
using (var stream = new FileStream(filename))
{
if (somethingNotValid)
throw new Exception(...);
return ...;
}
}
И в твоем главном:
{
try
{
var value = ParseFile(filename);
}
catch (Exception)
{
Console.WriteLine(..);
}
}
Я думаю, что лучший способ убедиться, что файловые потоки расположены, - обернуть их использование следующим using
блок
using (FileStream)
{
....
}
Использовать using
ключевое слово. С using
Вы можете переписать что-то вроде этого:
public static int CountCars()
{
SqlConnection conn = new SqlConnection(connectionString);
try
{
SqlCommand cmd = conn.CreateCommand();
conn.Open();
try
{
cmd.CommandText = "SELECT COUNT(1) FROM Carsd";
return (int)cmd.ExecuteScalar();
}
finally
{
if(cmd != null)
cmd.Dispose();
}
}
finally
{
if(cmd != null)
conn.Dispose();
}
}
в это:
public static int CountCars()
{
using(SqlConnection conn = new SqlConnection(connectionString))
using(SqlCommand cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT COUNT(1) FROM Carsd";
return (int)cmd.ExecuteScalar();
}
}
Оба фрагмента кода при компиляции выдают точно такой же код IL. Примеры взяты из http://coding.abel.nu/2011/12/idisposable-and-using-in-c/ где я написал более подробную информацию об использовании и IDisposable
,