CA2000/CA2202 for Stream in a using statement

586 views Asked by At

CA2000 and CA2202 warnings have recently been the bane of my existence. What am I doing wrong here? I basically get a FileStream using File.Open and then pass it into a function that may return a new stream or may return the same stream. I then perform some more actions on my stream and then in my finally block I dispose the stream I was using if it was different.

I get two CA warnings. 2000 for fileStream in the using block and 2202 for changedStream in the finally block. What gives?

using (Stream fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    Stream changedStream = null;
    try
    {
        changedStream = someCondition ? fileStream : DoSomeActionThatMayReturnNewStream(fileStream);
        DoSomeMoreStuffWithStream(changedStream);
    }
    finally
    {
        if (changedStream != null && changedStream != fileStream)
        {
            changedStream.Dispose();
        }
    }
}
2

There are 2 answers

4
supercat On

Under what cases, if any, will DoSomeActionThatMayReturnNewStream dispose of the passed-in stream? If when it creates a new stream it disposes of the passed-in one (which would generally be expected), the Dispose triggered by the using block will be redundant.

It appears as though the behavior of your code might be correct if DoSomeActionThatMayReturnNewStream never disposes of the passed-in stream, but FxCop has no way of analyzing its complex and unorthodox pattern of object ownership. I would suggest that it would be better to do something like

Stream inputFile = null;
try
{
  inputFile = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  DoSomeActionThatMayReturnNewStream(ref inputFile);
  DoSomeMoreStuffWithStream(inputFile);
}
finally
{
  if (inputFile != null)
    inputFile.Dispose();
}

The DoSomeActionThatMayReturnNewStream should dispose of the old stream if it's going to open a new one. It should null the variable immediately before closing the old stream and and assign it immediately upon opening the new one. That will ensure that if an exception occurs during the method, the old stream will get disposed if and only if it hasn't been disposed before, and the new stream will get disposed if its constructor completed, even if the DoSomeActionThatMayReturnNewStream threw an exception after that [if that method calls Dispose on the new stream in case an exception gets thrown, it should null out the variable in such case].

2
John Saunders On

What's wrong with the following:

using (var fileStream = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (var changedStream = someCondition ? fileStream : DoSomeActionThatMayReturnNewStream(fileStream))
    {
        DoSomeMoreStuffWithStream(changedStream);
    }
}