繁体   English   中英

为什么FxCop没有报告CA2000这个非处理类实例的琐碎案例?

[英]Why does FxCop not report CA2000 for this trivial case of not-disposed class instance?

以下代码为Main的第一行生成CA2000 (“ 丢失范围之前的Dispose对象 ”)违规,但不生成第二行。 我真的很喜欢第二行的CA2000违规,因为这是我工作的大型代码库中常见的(显然简化的)模式。

有谁知道为什么第二行没有产生违规?

public static void Main()
{
    new Disposable();
    MakeDisposable();
}

private static Disposable MakeDisposable() { return new Disposable(); }

private sealed class Disposable : IDisposable
{
    public void Dispose()
    {
    }
}

简短的回答是,CA2000已经破裂,不太可能很快得到修复。 请参阅此错误报告 ,这几乎是您的问题:

特别是警告CA2000是一个已知问题的规则,我们将无法以当前形式修复它。


更长的答案是让CA2000正确是很难的 在过去的Visual Studio版本中,特别是2010年,在整个地方都发出了错误的CA2000警告,并且没有任何办法可以让它们消失。 搜索Stack Overflow以查找有关它的任何问题。

即使在您可以消除警告的情况下,解决方法也几乎比将其留在原地更糟糕。 问题是,在您遇到的情况下,您希望在它离开工厂方法范围之前处置对象。 除此之外,你会这样做 - 如果它抛出异常 在这种情况下,方法的返回值将丢失,并且调用者无法为自己处置该对象。

不幸的是,试图弄清楚这意味着对编译的IL进行程序流分析,以确定是否有任何代码路径(包括特殊路径)允许对象泄漏。 最终的结果是,几乎任何你试图从方法返回IDisposable的情况都会产生错误。

微软通过做两件事做出了回应:

  • 降低CA2000的灵敏度,使其仅在最明显的情况下触发。 这似乎是合理的,因为像你的第一行这样的明显案例比更模糊的案例更容易出错,而且更容易修复。
  • 使CA2000超出其推荐的代码分析规则; 请注意,他们的“扩展正确性”规则集不再包含CA2000。

与Roslyn编译器重写相比,FxCop等工具可以进行一些源级分析,在这种情况下可能更容易正确。 同时,普遍的共识是,关闭CA2000。


如果您感到好奇,一些测试似乎确认当前(2013)CA规则在本地包含的本地构造的IDisposable实例超出范围时触发。 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. MakeDisposable本身很好

由于MakeDisposable没有错误,因为它为什么会这样? 你可以有

using ( var disposable = MakeDisposable() )
{
    // elided
}

代码中的任何位置。


2.在该引用 IDisposableMakeDisposable不考虑对Main

Main方法中调用MakeDisposable()不会导致错误,因为编译器不知道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()中,使用new Disposable();实例化IDisposable new Disposable(); 编译器知道你没有将它传递出该范围,因此正确地给出了错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM