Streamwriter CA2202: не удаляйте объекты несколько раз

У меня есть фрагмент кода, который я использую при отладке для записи строки информации в файл.

    private bool appendLine(string line2Write, string fileName)
    {
        try
        {
            StreamWriter tw;
            using (tw = File.AppendText(fileName))
            {
                tw.WriteLine(line2Write);
                tw.Close();
            }
        }
        catch (Exception ex)
        {
            DialogResult result = MessageBox.Show("Unable to write to: " + fileName + "\r\n" + ex.ToString() + "\r\n OK to retry", "File Sysytem Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
            if (result == DialogResult.Cancel)
            {
                return false;
            }
        }
        return true;
    }

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

Я, вероятно, не понимаю, что мне говорит CA2202.

Вот весь текст ошибки:

Предупреждение CA2202. Объект «tw» может быть удален более одного раза в методе «familyFinances.appendLine(string, string)». Чтобы избежать создания System.ObjectDisposedException, вы не должны вызывать Dispose для объекта более одного раза.

"tw" существует только в этом коде. И у меня никогда не было ошибок при запуске таким образом.

Варианты или предложения?


person user3279899    schedule 04.10.2018    source источник
comment
tw.Close(); — первое удаление, выход из блока using — второе.   -  person dymanoid    schedule 04.10.2018


Ответы (2)


Вы звоните Close и Dispose. Вы вызываете Close явно и Dispose неявно через оператор using. Эти два эквивалентны, у вас должен быть только один из них.

Это не вызовет предупреждение:

private bool appendLine(string line2Write, string fileName)
{
    try
    {
        StreamWriter tw;
        using (tw = File.AppendText(fileName))
        {
            tw.WriteLine(line2Write);
        }
    }
    catch (Exception ex)
    {
        DialogResult result = MessageBox.Show("Unable to write to: " + fileName + "\r\n" + ex.ToString() + "\r\n OK to retry", "File Sysytem Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
        if (result == DialogResult.Cancel)
        {
            return false;
        }
    }
    return true;
}

В описании правила явно указано, что Close и Dispose являются рассматривается

Реализация метода содержит пути кода, которые могут вызвать несколько вызовов System.IDisposable.Dispose или эквивалента Dispose, например метода Close() для некоторых типов, для одного и того же объекта.

Хотя в этом случае объект не будет жаловаться на двойное удаление, нет никакой реальной причины сохранять и то, и другое, так что это все еще хорошая уловка в отношении стиля кода.

person Titian Cernicova-Dragomir    schedule 04.10.2018
comment
Это то, что было неочевидным, по крайней мере, для меня. И это может объяснить проблемы в других частях кода, которые я использовал. Спасибо. - person user3279899; 06.10.2018

Как уже упоминалось, эта проблема вызвана тем, что вы вызываете Close() внутри блока using, и этот вызов следует удалить. Я предлагаю вам покопаться и понять, почему эти вызовы эквивалентны.

Посмотрите на исходный код StreamWriter.Close():

public override void Close() {
   Dispose(true);
   GC.SuppressFinalize(this);
}

И метод IDisposable.Dispose(), который TextWriter (база для StreamWriter) реализует, как показано ниже. Этот Dispose() вызывается средой выполнения при выполнении закрывающей фигурной скобки блока using.

public void Dispose() {
    Dispose(true);
    GC.SuppressFinalize(this);
}

Компилятор переводит блок using в try/finally, поэтому рассматриваемый код эквивалентен:

StreamWriter tw = File.AppendText(fileName)
try {
    tw.WriteLine(line2Write);
    tw.Close();
}
finally {
    tw.Dispose();
}

Таким образом, вы делаете одно и то же дважды и, таким образом, получаете это предупреждение.

К сведению: исходный код .NET framework здесь

person Nikhil Vartak    schedule 04.10.2018