The following code produces a CA2000 ("Dispose objects before losing scope") violation for the first line of Main, but not the second. I would really like the CA2000 violation for the second line, because this is a (obviously simplified) pattern often found in a large code base I work on.
Does anyone know why the violation is not produced for the second line?
public static void Main()
{
new Disposable();
MakeDisposable();
}
private static Disposable MakeDisposable() { return new Disposable(); }
private sealed class Disposable : IDisposable
{
public void Dispose()
{
}
}
The short answer is, CA2000 is broken and not likely to get fixed anytime soon. See this bug report which is almost exactly your question:
The warning CA2000 in particular is a rule that has known issues and that we won’t be able to fix it up in its current form.
The longer answer is that getting CA2000 right is hard. In past versions of Visual Studio, particularly 2010, false CA2000 warnings fired all over the place, and there was nothing you could do to make them go away. Search Stack Overflow for any of the dozens of questions about it.
Even in cases where you could eliminate the warning, the workaround was almost worse than just leaving it in place. The problem is, in cases like the one you have here, is that you don't want the object disposed before it leaves scope of the factory method. Except, you do -- if it throws an exception. In that case, the return value of the method is lost, and the caller has no way to dispose the object for itself.
Unfortunately, trying to figure that out means doing program flow analysis of the compiled IL, to determine if any code path (including exceptional ones) allow the object to leak. The end result was, almost any case where you tried to return an IDisposable
from a method would produce the error.
Microsoft responded to this by doing two things:
Along with the Roslyn compiler rewrite comes the ability for tools like FxCop to do some source-level analysis, which may be easier to get correct in this case. In the mean time, the general consensus is, just turn CA2000 off.
In case you're curious, a bit of testing seems to confirm that the current (2013) CA rule only fires if a locally contained, locally constructed instance of IDisposable
drops out of scope. IOW, the object can't leave the method in which you new
it up, or CA ignores it. This leads me to believe that CA is simply not digging into method calls for it's analysis. Besides trying to silence false positives, it may also have been part of the overall attempt to speed up the CA process by cutting out some expensive checks, which I believe happened between 2010 and 2012?
If I add bit to your example, you can see the obvious pattern of which ones get the warning:
class Program
{
public static void Main()
{
new Disposable(); // CA2000
IDisposable x;
MakeDisposable(out x);
Test();
}
public static Disposable Test()
{
IDisposable z;
var x = MakeDisposable(out z);
var y = new Disposable(); // CA2000
return x;
}
private static Disposable MakeDisposable(out IDisposable result)
{
result = new Disposable();
new Disposable(); // CA2000
return new Disposable();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With