[英]Why does FxCop not report CA2000 for this trivial case of not-disposed class instance?
The following code produces a CA2000 (" Dispose objects before losing scope ") violation for the first line of Main, but not the second. 以下代码为Main的第一行生成CA2000 (“ 丢失范围之前的Dispose对象 ”)违规,但不生成第二行。 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. 我真的很喜欢第二行的CA2000违规,因为这是我工作的大型代码库中常见的(显然简化的)模式。
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. 简短的回答是,CA2000已经破裂,不太可能很快得到修复。 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. 特别是警告CA2000是一个已知问题的规则,我们将无法以当前形式修复它。
The longer answer is that getting CA2000 right is hard . 更长的答案是让CA2000正确是很难的 。 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. 在过去的Visual Studio版本中,特别是2010年,在整个地方都发出了错误的CA2000警告,并且没有任何办法可以让它们消失。 Search Stack Overflow for any of the dozens of questions about it. 搜索Stack Overflow以查找有关它的任何问题。
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. 不幸的是,试图弄清楚这意味着对编译的IL进行程序流分析,以确定是否有任何代码路径(包括特殊路径)允许对象泄漏。 The end result was, almost any case where you tried to return an IDisposable
from a method would produce the error. 最终的结果是,几乎任何你试图从方法返回IDisposable
的情况都会产生错误。
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. 与Roslyn编译器重写相比,FxCop等工具可以进行一些源级分析,在这种情况下可能更容易正确。 In the mean time, the general consensus is, just turn CA2000 off. 同时,普遍的共识是,关闭CA2000。
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. 如果您感到好奇,一些测试似乎确认当前(2013)CA规则仅在本地包含的本地构造的IDisposable
实例超出范围时触发。 IOW, the object can't leave the method in which you new
it up, or CA ignores it. IOW,该对象不能离开您new
它的方法,或CA忽略它。 This leads me to believe that CA is simply not digging into method calls for it's analysis. 这让我相信CA根本就没有深入研究它的分析方法调用。 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? 除了试图消除误报之外,它还可能是通过削减一些昂贵的支票来加速CA流程的整体尝试的一部分,我相信这些支票发生在2010年和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();
}
}
I have a hunch that this happens due to a combination of factors. 我预感到这种情况会因多种因素共同作用而发生。
1. MakeDisposable
is fine by itself : 1. MakeDisposable
本身很好 :
There is no error due to MakeDisposable
because, well why would it? 由于MakeDisposable
没有错误,因为它为什么会这样? You could have 你可以有
using ( var disposable = MakeDisposable() )
{
// elided
}
anywhere in your code. 代码中的任何位置。
2. The references to the IDisposable
in MakeDisposable
is not taken into account on Main
2.在该引用 IDisposable
在MakeDisposable
不考虑对Main
The call to MakeDisposable()
in your Main
method does not cause an error because the compiler does not know that the return value of MakeDisposable
is the only reference to the IDisposable
. 在Main
方法中调用MakeDisposable()
不会导致错误,因为编译器不知道MakeDisposable
的返回值是对IDisposable
的唯一引用。
In other words, in the eyes of the compiler, the following forms of MakeDisposable()
are equivalent: 换句话说,在编译器的眼中,以下形式的MakeDisposable()
是等效的:
private static Disosable MakeDisposable()
{
return new Disosable();
}
(the original), or exposing a backing field, creating it if necessary: (原始),或暴露支持字段,必要时创建它:
private static Disposable disposable;
private static Disposable MakeDisposable()
{
if ( disposable == null )
disposable = new Disposable();
return disposable;
}
private static void DisposeDisposable()
{
// elided
}
or even exposing a backing field already created: 甚至暴露已经创建的支持字段:
private static Disposable disposable = new Disposable();
private static Disposable MakeDisposable()
{
return disposable;
}
private static void DisposeDisposable()
{
// elided
}
so the call in Main
is valid. 所以Main
的呼叫是有效的。
In Main()
, where you are instantiating the IDisposable
, with new Disposable();
在Main()
中,使用new Disposable();
实例化IDisposable
new Disposable();
the compiler knows that you're not passing it out of that scope so correctly gives the error. 编译器知道你没有将它传递出该范围,因此正确地给出了错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.