以下代码为Main的第一行产生CA2000(“在丢失范围之前设置对象”)违规,但不是第二行。我真的很喜欢第二行的CA2000违规,因为这是我工作的大型代码库中常见的(显然简化的)模式。
有谁知道为什么第二行没有产生违规行为?
public static void Main()
{
new Disposable();
MakeDisposable();
}
private static Disposable MakeDisposable() { return new Disposable(); }
private sealed class Disposable : IDisposable
{
public void Dispose()
{
}
}
答案 0 :(得分:6)
简短的回答是,CA2000已经破裂,不太可能很快得到修复。请参阅this bug report,这几乎是您的问题:
警告CA2000特别是一个已知问题的规则,我们将无法以当前形式修复它。
更长的答案是让CA2000正确硬。在过去的Visual Studio版本中,特别是2010年,在整个地方都发出了错误的CA2000警告,并且没有任何办法可以让它们消失。搜索Stack Overflow中有关于它的任何问题。
即使在您可以消除警告的情况下,解决方法也几乎比将其留在原地更糟糕。问题是,在你这里的情况下,你不希望希望在它离开工厂方法的范围之前处置对象。除此之外,你会 - 如果它抛出异常。在这种情况下,方法的返回值将丢失,并且调用者无法为自己处置该对象。
不幸的是,试图弄清楚这意味着对已编译的IL进行程序流分析,以确定是否有任何代码路径(包括特殊路径)允许对象泄漏。最终的结果是,几乎任何你试图从方法返回IDisposable
的情况都会产生错误。
微软通过做两件事做出了回应:
与Roslyn编译器重写相比,FxCop等工具可以进行一些源级分析,在这种情况下可能更容易正确。与此同时,普遍的共识是,只需关闭CA2000即可。
如果您感到好奇,一点测试似乎确认当前包含的,本地构建的IDisposable
实例退出时,当前(2013)CA规则仅将触发范围。 IOW,该对象不能离开您new
它的方法,或CA忽略它。这让我相信CA根本就没有深入研究它的分析方法调用。除了试图压制误报之外,它还可能是通过削减一些昂贵的支票来加速CA流程的整体尝试的一部分,我认为这些检查发生在2010年和2012年之间?
如果我在你的例子中添加了一些内容,你可以看到哪些明显的模式会得到警告:
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();
}
}
答案 1 :(得分:1)
我预感到这种情况会因各种因素而发生。
<强> 1。 MakeDisposable
本身很好:
由于MakeDisposable
没有错误,因为它为什么会这样?你可能有
using ( var disposable = MakeDisposable() )
{
// elided
}
代码中的任何位置。
<强> 2。 IDisposable
MakeDisposable
中Main
的引用
MakeDisposable()
方法中对Main
的调用不会导致错误,因为编译器不知道MakeDisposable
的返回值仅为 引用IDisposable
。
换句话说,在编译器看来,MakeDisposable()
的以下形式是等价的:
private static Disosable MakeDisposable()
{
return new Disosable();
}
(原始),或暴露支持字段,必要时创建它:
private static Disposable disposable;
private static Disposable MakeDisposable()
{
if ( disposable == null )
disposable = new Disposable();
return disposable;
}
private static void DisposeDisposable()
{
// elided
}
甚至公开已创建的支持字段:
private static Disposable disposable = new Disposable();
private static Disposable MakeDisposable()
{
return disposable;
}
private static void DisposeDisposable()
{
// elided
}
因此Main
中的通话有效。
在Main()
中,您使用IDisposable
实例化new Disposable();
,编译器知道您没有将其传递出该范围所以正确地给出了错误。