С# 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(), когда объект выходит из области видимости в конце блока.