Использование оператора FileStream и/или StreamReader - Предупреждения Visual Studio 2012

Новая Visual Studio 2012 жалуется на общую кодовую комбинацию, которую я всегда использовал. Я знаю, что это кажется излишним, но я сделал следующее в своем коде, чтобы быть уверенным.

using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (var sr = new StreamReader(fs))
    {
        // Code here
    }
}

Visual studio "предупреждает" меня, что я избавляюсь от fs более одного раза. Поэтому мой вопрос в том, правильно ли это записать:

using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    var sr = new StreamReader(fs);
    // do stuff here
}

Или я должен делать это таким образом (или какой-либо другой вариант, не упомянутый).

var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

using (var sr = new StreamReader(fs))
{
    // Code here
}

Я искал несколько вопросов в StackOverflow, но не нашел того, что напрямую касалось лучшей практики для этой комбинации.

Спасибо!

Ответ 1

Ниже описывается, как рекомендует Microsoft делать это. Он длинный и громоздкий, но безопасный:

FileStream fs = null;
try
{
    fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    using (TextReader tr= new StreamReader(fs))
    {
        fs = null;
        // Code here
    }
}
finally
{
    if (fs != null)
        fs.Dispose();
}

Этот метод всегда гарантирует, что все будет устранено, что должно быть, несмотря на то, что могут быть выбраны исключения. Например, если конструктор StreamReader генерирует исключение, FileStream все равно будет правильно размещен.

Ответ 2

Visual studio "предупреждает" меня, что я избавляюсь от fs более одного раза.

Вы, но это нормально. Документация для IDisposable.Dispose гласит:

Если объект Dispose вызывается более одного раза, объект должен игнорировать все вызовы после первого. Объект не должен генерировать исключение, если его метод Dispose вызывается несколько раз.

Исходя из этого, предупреждение является фиктивным, и мой выбор заключается в том, чтобы оставить код таким, какой он есть, и подавить предупреждение.

Ответ 3

Как говорит Дэн только для работы с StreamWriter, я считаю, что это наиболее приемлемый ответ. (Ответ Дэна по-прежнему будет давать дважды предупреждающее сообщение StreamReader, как упоминает Даниэль Хильгарт и обостренный эксперемтер, StreamReader располагает файловым потоком)

using (TextReader tr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
    string line;
    while ((line = tr.ReadLine()) != null)
    {
        // Do work here
    }
}

Это очень похоже на ответ Даниэля Хильгарта, измененный для вызова dispose с помощью инструкции Using в StreamReader, поскольку теперь стало ясно, что StreamReader вызовет dispose в FileStream (согласно всем другим сообщениям, на которые ссылается документация)

Update:

Я нашел этот пост. Для чего это стоит. Устраняет ли поток streamreader поток?

Ответ 4

Да, правильным способом было бы использовать вашу первую альтернативу:

using (FileStream fs = new FileStream(filePath, FileMode.Open,
                                      FileAccess.Read, FileShare.ReadWrite)) 
{ 
    TextReader tr = new StreamReader(fs); 
    // do stuff here 
} 

Причина заключается в следующем:
Утилизация StreamReader ограничивает только FileStream так, что на самом деле это единственное, что вам нужно распоряжаться.

Ваш второй вариант (только внутренний "использование" ) не является решением, так как он оставил бы FileStream unisposed, если в конструкторе StreamReader было исключение.

Ответ 5

Это потому, что способ, которым вы использовали StreamReader, удаляет поток, когда он находится. Таким образом, если вы также уничтожаете поток, он будет удален дважды. Некоторые считают это недостатком в StreamReader, но там нет. В VS 2012 (.NET 4.5) есть опция в StreamReader, чтобы не избавиться от потока, с новым конструктором: http://msdn.microsoft.com/en-us/library/gg712952

Ответ 6

Два решения:

A). Вы доверяете рефлектору или документации, и знаете, что *Reader и *Writer закроют базовый *Stream. Но предупреждение: оно не будет работать в случае брошенного Исключения. Поэтому это не рекомендуется:

using (TextReader tr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
    // Code here
}

B). Вы игнорируете предупреждение, поскольку состояния документации The object must not throw an exception if its Dispose method is called multiple times. Это рекомендуемый способ, так как и хорошая практика всегда использовать using, и безопасно в случае брошенного исключения:

[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
internal void myMethod()
{
    [...]
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (TextReader tr = new StreamReader(fs))
    {
        // Code here
    }
}

Ответ 7

Учитывая всю эту чушь, этот (совершенно законный!) вопрос породил, это было бы моим предпочтением:

FileStream fs = null;
TextReader tr= null;
try
{
    fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    tr= new StreamReader(fs);
    // Code here
}
finally
{
    if (tr != null)
        tr.Dispose();
    if (fs != null)
        fs.Dispose();
}

Нижеприведенные ссылки иллюстрируют совершенно законный синтаксис. ИМО, этот синтаксис "использования" гораздо предпочтительнее вложенного "использования". Но, признаюсь, это не решает вопрос:

ИМХО...