CA2202: Do not dispose objects multiple times
TypeName | DoNotDisposeObjectsMultipleTimes |
CheckId | CA2202 |
Category | Microsoft.Usage |
Breaking Change | Non Breaking |
A method implementation contains code paths that could cause multiple calls to IDisposable.Dispose or a Dispose equivalent, such as a Close() method on some types, on the same object.
A correctly implemented Dispose method can be called multiple times without throwing an exception. However, this is not guaranteed and to avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.
To fix a violation of this rule, change the implementation so that regardless of the code path, Dispose is called only one time for the object.
Do not suppress a warning from this rule. Even if Dispose for the object is known to be safely callable multiple times, the implementation might change in the future.
Nested using statements (Using in Visual Basic) can cause violations of the CA2202 warning. If the IDisposable resource of the nested inner using statement contains the resource of the outer using statement, the Dispose method of the nested resource releases the contained resource. When this situation occurs, the Dispose method of the outer using statement attempts to dispose its resource for a second time.
In the following example, a Stream object that is created in an outer using statement is released at the end of the inner using statement in the Dispose method of the StreamWriter object that contains the stream object. At the end of the outer using statement, the stream object is released a second time. The second release is a violation of CA2202.
using (Stream stream = new FileStream("file.txt", FileMode.OpenOrCreate))
{
using (StreamWriter writer = new StreamWriter(stream))
{
// Use the writer object...
}
}
To resolve this issue, use a try/finally block instead of the outer using statement. In the finally block, make sure that the stream resource is not null.
Stream stream = null;
try
{
stream = new FileStream("file.txt", FileMode.OpenOrCreate);
using (StreamWriter writer = new StreamWriter(stream))
{
stream = null;
// Use the writer object...
}
}
finally
{
if(stream != null)
stream.Dispose();
}
The Dispose pattern can be confusing to many novices, and I've seen code where resources are placed in a using statement, but the programmer went ahead and called Dispose (or a similar method -- Close on streams for example) withing the using block. Albeit redundent, this should be perfectly safe code to write, but isn't because using statements cannot check the dispose state of the object.
As someone stated early, the compiler should be able to track resources for us. Currently, it cannot because it will always assume the resources in a using statement will only ever be Disposed by the statement.
One fix would be to add an IsDisposed property on the IDisposable interface. Most implementation of a dispose method rely on some sort of similar bool already. This way, the compiler can check this property at the end of the using statement scoop before it calls Dispose.
- 4/12/2012
- vxsery
byte[] result;
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms, Encoding.UTF8))
{ bw.Write(data);
bw.Flush();
}
result= ms.ToArray();
}
It is a very odd behavior, as data is accessed after an implicit Close (Dispose), but it's the way that work some writers.
- 12/20/2011
- Cesc.S
- 4/2/2012
- Thomas Lee
Rather than have a zillion people work around this with silly rules why not just fix the compiler? If the rules checker can find it then the compiler can and properly nest the Dispose.
Compilers should manage resources and memory, not humans.
- 8/17/2011
- Grin
- 7/15/2011
- Mihail Arhipov
- 4/6/2011
- A. Mereaux
- 4/29/2011
- Jaecen
I don't disagree:-) But if you want to get results such as a revision or removal of the rule, posting on the forum or better yet, creating a Connect bug, is the way to go. The code-analysis team is small and over-worked, so a suggestion from the doc writer is not high on their radar. Connect bugs are. I would especially encourage it for this rule.
As the moderator of the code analysis pages, I won't remove these posts, but I would suggest that you write your critique of the rule and then link to a forum question or Connect bug.
Patrick - a doc writer
- 1/25/2011
- Patrick Sheahan
As a user, I am glad to have this feedback here. When I saw a violation of this rule, the first thing I did was come to this page to see if anybody else thought the rule was crazy too. While I realize this location is not the ideal for in depth discussions or communicating feedback to the product team, nonetheless I am glad to have some record of this issue here where it is easily visible in the most likely place a user would check to find it.
- 1/19/2011
- dmatson
- 11/11/2010
- Patrick Sheahan
- 9/24/2010
- MagiesH
As previously stated, nested using statements should be encouraged rather than a try / catch / finally syntax.
- 8/11/2010
- cor2879
- 5/26/2010
- Anonymous629
- 5/26/2010
- shepard
As stated in the "Rule Description" secion of this article, "A correctly implemented Dispose method can be called multiple times without throwing an exception." This is also mentioned in the article "Implementing Finalize and Dispose to Clean Up Unmanaged Resources" (http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx). So an exception will not be thrown if an object is disposed multiple times unless one of these conditions is true:
- I have failed to implement a "correctly implemented Dispose method" in my code. In this case, I should correct the code in that Dispose method.
- Microsoft has failed to implement a "correctly implemented Dispose method" in the CLR. This would be a horrific mistake on Microsoft's part and I would hope this would never be the case.
- A third party vendor has provided a component that fails to implement a "correctly implemented Dispose method." In this case, I would think contacting the vendor about the problem and having them fix it would be the correct thing to do.
In any event, the solution of dropping the outer using statement in favor of the try/finally construct makes the code much less readable. This is espeically true when there are three or more nested using statements. It seems ludicrous to write less readable code to account for the case of an incorrectly implemented Dispose method. Instead, I would think the more readable nested using statements would be preferred, and the try/finally construct can be used for cases where a third party vendor did not implemnt Dispose correctly and will not provide a corrected version.
The bottom line is that this rule is indended to avoid an ObjectDisposedException thrown from a Dispose() method which should never happen. As such, the existence of CA2202 makes little sense.
- 5/14/2010
- JoeM27