[英]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
的情況都會產生錯誤。
微軟通過做兩件事做出了回應:
與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.在該引用 IDisposable
在MakeDisposable
不考慮對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.