簡體   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