С# StreamReader в try/finally
У меня сегодня есть вопрос, связанный с классом StreamReader. В частности, инициализация этого класса с использованием параметра имени файла, например:
TextReader tr = new StreamReader(fileName);
Очевидно, когда это действие закончено, важно закрыть поток следующим образом:
tr.Close();
Я хотел бы иметь это в попытке /, наконец, проблема в том, что я не могу найти способ сделать это. Вот некоторые варианты, которые я нашел, что НЕ работают:
try
{
var serializer = new XmlSerializer(type);
TextReader tr = new StreamReader(fileName);
var obj = serializer.Deserialize(tr);
}
finally
{
tr.Close();
}
и хуже:
TextReader tr;
try
{
var serializer = new XmlSerializer(type);
tr = new StreamReader(fileName);
var obj = serializer.Deserialize(tr);
}
finally
{
tr.Close();
}
Итак, возможно ли иметь StreamReader в конце?
Ответы
Ответ 1
Самый простой способ - использовать оператор using
:
using (TextReader tr = new StreamReader(fileName))
{
// ...
}
Компилятор сгенерирует для вас блок try-finally и поместит код для вызова Close (фактически Dispose) в конце.
Если вам нужно явно указать finally
, ваш второй пример будет работать, за исключением того, что вам нужно принудительно инициализировать tr
:
TextReader tr = null;
И вы, конечно же, захотите проверить tr != null
внутри своего блока finally, если произошло исключение до того, как был выполнен tr = new StreamReader(...)
.
Ответ 2
Да, либо:
TextReader tr = null;
try
{
var serializer = new XmlSerializer(type);
tr = new StreamReader(fileName);
var obj = serializer.Deserialize(tr);
}
finally
{
if (tr != null)
tr.Close();
}
или просто:
using (TextReader tr = new StreamReader(fileName))
{
var serializer = new XmlSerializer(type);
var obj = serializer.Deserialize(tr);
}
Причина, по которой первая часть кода в вашем вопросе не скомпилировалась, заключалась в том, что в блоке try была объявлена переменная tr
, что сделало ее недоступной для finally-блока.
Причина, по которой вторая часть кода в вашем вопросе не скомпилировалась, заключалась в том, что переменной tr
не было присвоено значение, и если new XmlSerializer
должен был выдать исключение, он не получил бы одного в попытке -блочный, что означает, что у вас есть возможное значение undefined в переменной, когда оно достигает finally-блока.
Решение, если вы хотите полностью сохранить структуру try/finally, состоит в том, чтобы гарантировать, что переменная имеет начальное значение, и вызывать только .Close
, если она изменилась.
Конечно, наиболее правильным способом является использование блока using
, который заботится обо всех деталях для вас. Обратите внимание, что это изменит порядок построения между читателем и объектом сериализатора, или вы можете иметь такой код, который сохраняет порядок, который у вас был в вашем вопросе (в данном случае это не имеет значения):
var serializer = new XmlSerializer(type);
using (TextReader tr = new StreamReader(fileName))
{
var obj = serializer.Deserialize(tr);
}
Ответ 3
Комментарий к этому коду
TextReader tr;
try
{
var serializer = new XmlSerializer(type);
tr = new StreamReader(fileName);
var obj = serializer.Deserialize(tr);
}
finally
{
//tr.Close();
// Correct way
if (tr != null) tr.Close();
}
Что произойдет, если StreamReader
завершится сбой, тогда выполняется блок finally
, теперь здесь tr
по-прежнему null
в результате отказа, вы получите исключение внутри finally
block!
Предпочтительнее поймать System.IO.IOException
, чтобы вы правильно обрабатывали ситуацию, а не маскировали проблему с помощью ввода/вывода файлов...
Ответ 4
Не используйте try/finally здесь, используйте оператор Using, который делает то же самое, что и вы, используя более чистый синтаксис. Ответ "да", кстати:)
Ответ 5
вы должны использовать ключевое слово using
.
using (TextReader tr = new StreamReader(fileName))
{
var obj = serializer.Deserialize(tr);
}
будет вызываться метод Dispose(), когда объект выходит из области видимости в конце блока.