为啥 FxCop 不报告 CA2000 对于这种未处置的类实例的琐碎情况?
Posted
技术标签:
【中文标题】为啥 FxCop 不报告 CA2000 对于这种未处置的类实例的琐碎情况?【英文标题】:Why does FxCop not report CA2000 for this trivial case of not-disposed class instance?为什么 FxCop 不报告 CA2000 对于这种未处置的类实例的琐碎情况? 【发布时间】:2015-04-12 20:14:11 【问题描述】:以下代码对 Main 的第一行产生了 CA2000 ("Dispose objects before lost scope") 违规,但不是第二行。我真的很喜欢第二行的 CA2000 违规,因为这是我工作的大型代码库中经常发现的(明显简化的)模式。
有谁知道为什么第二行没有产生违规?
public static void Main()
new Disposable();
MakeDisposable();
private static Disposable MakeDisposable() return new Disposable();
private sealed class Disposable : IDisposable
public void Dispose()
【问题讨论】:
尝试将您的 Make 方法重命名为 Create,看看是否会有所不同。 还有...您使用的是哪个版本的 Visual Studio 和/或 FxCop? 我试着即兴创作一些对你有用的东西,但下次请尝试制作一个更具描述性的标题。 对不起,我应该提一下,这是 Visual Studio 2013 的。 【参考方案1】:简短的回答是,CA2000 坏了,不太可能很快得到修复。请参阅this bug report,这几乎正是您的问题:
尤其是警告 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();
【讨论】:
我想这就是我所期望的。然而,我的抱怨并不是因为工厂没有被标记(我们有很好的模式来确保工厂不泄漏),而是 Main 没有被标记的事实。确保工厂的所有调用者都在泄漏要困难得多。我想我太天真了,但我希望如果这条规则捕捉到任何东西,它将能够捕捉到 Main 方法中的两条线。【参考方案2】:我有一种预感,这种情况的发生是多种因素造成的。
1. MakeDisposable
本身就很好:
MakeDisposable
没有错误,因为,为什么会这样?你可以有
using ( var disposable = MakeDisposable() )
// elided
代码中的任何位置。
2。在MakeDisposable
中对IDisposable
的引用未在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()
中,您正在实例化IDisposable
,而new Disposable();
编译器知道您没有将它正确地传递出该范围会给出错误。 p>
【讨论】:
以上是关于为啥 FxCop 不报告 CA2000 对于这种未处置的类实例的琐碎情况?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 FxCop 会在此 C# 代码中发出有关溢出 (CA2233) 的警告?
基于不适用于 System.Web.UI.Control 对象的 CA2000“在失去范围之前处理对象”创建自定义 FXCop 规则
.NET Framework FxCop 规则 CA1401 PInvokesShouldNotBeVisible 规则 - 为啥存在此规则?