Streamwriter CA2202: Do not dispose objects multiple times

344 views Asked by At

I have a piece of code I use while debugging to write a line of information to a file.

    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;
    }

I don't want to leave the file open because, if it is debugging information I don't want to risk the last bit if the program crashes.

I am probably not understanding what the CA2202 is telling me.

Here's the whole error statement:

Warning CA2202 Object 'tw' can be disposed more than once in method 'familyFinances.appendLine(string, string)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.

"tw" only exists in this code. And, I've never had an error running it this way.

Options or suggestions?

2

There are 2 answers

1
Titian Cernicova-Dragomir On BEST ANSWER

You call Close and Dispose. You call Close explicitly and Dispose implicitly trough the using statement. The two are equivalent, you should only have one of them.

This will not throw the warning:

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;
}

The rule description explicitly states that Close and Dispose are both considered

A method implementation contains code paths that could cause multiple calls to System.IDisposable.Dispose or a Dispose equivalent, such as a Close() method on some types, on the same object.

While in this case the object will not complain about the double dispose, there is no real reason to keep both, so it's still a good catch with regard to code style.

0
Nikhil Vartak On

As others already mentioned this problem is caused because you are calling Close() inside using block, and that call should be removed. I suggest you dig and understand why these calls are equivalent.

Look at the StreamWriter.Close() source code:

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

And the IDisposable.Dispose() method which TextWriter (base for StreamWriter) implements as below. This Dispose()is called by runtime when closing curly brace of using block executes.

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

Compiler translates using block to try/finally, so your code in question is equivalent to:

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

So you are doing same thing twice, and thus getting that warning.

FYI - .NET framework source code here