告诉FxCop另一种方法是调用dispose

时间:2012-06-28 17:06:15

标签: c# idisposable fxcop

通常,当您处置私有成员时,您可能会执行以下操作:

public void Dispose() {
    var localInst = this.privateMember;
    if (localInst != null) {
        localInst.Dispose();
    }
}

本地赋值的目的是避免竞争条件,其中另一个线程可能在null检查后将私有成员分配为null。在这种情况下,我不在乎是否在实例上调用了Dispose两次。

我一直使用这种模式,所以我写了一个扩展方法来做到这一点:

public static void SafeDispose(this IDisposable disposable)
{
    if (disposable != null)
    {
        // We also know disposable cannot be null here, 
        // even if the original reference is null.
        disposable.Dispose();
    }
}

现在在我的课堂上,我可以这样做:

public void Dispose() {
    this.privateMember.SafeDispose();
}

问题是,FxCop不知道我这样做了,它在每种情况下都给我CA2000: Dispose objects before losing scope警告。

我不想关闭这个规则而且我不想压制每一个案例。有没有办法向FxCop暗示这个方法相当于Dispose就其而言?

3 个答案:

答案 0 :(得分:8)

简短的回答是:没有办法暗示对象被放置在其他地方。

一点点Reflector-ing(或dotPeek-ing,或其他)解释了原因。

FxCop位于C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop。 (根据您的OS / VS版本组合进行相应调整。)规则位于Rules子目录中。

在主FxCop文件夹中,打开

  • Microsoft.VisualStudio.CodeAnalysis.dll
  • Microsoft.VisualStudio.CodeAnalysis.Phoenix.dll
  • phx.dll

Rules文件夹中,打开DataflowRules.dll

DataflowRules.dll中找到Phoenix.CodeAnalysis.DataflowRules.DisposeObjectsBeforeLosingScope。这是进行评估的实际班级。

查看其中的代码,您可以看到与您的问题有关的两件事。

  1. 它使用名为SharedNeedsDisposedAnalysis 的共享服务。
  2. 来自FunctionBodyRule
  3. 第一项很有意思,因为 SharedNeedsDisposedAnalysis决定了哪些符号需要Dispose()调用。这是非常彻底的,通过代码“行走”来确定需要什么被处置和实际处置的东西。然后它会保留这些内容的表格供以后使用。

    第二项很有意思,因为 FunctionBodyRule规则会评估单个函数的正文。还有其他规则类型,例如FunctionCallRule,用于评估函数调用成员之类的内容(例如ProvideCorrectArgumentsToFormattingMethods)。

    关键是,SharedNeedsDisposedAnalysis服务中潜在的“未命中”,它可能不会通过您的方法递归,以确定事情实际上已被处理,FunctionBodyRule的限制不会超出功能正文,它只是没有捕捉你的扩展名。

    这就像Guard.Against<ArgumentNullException>(arg)之类的“保护函数”在使用之前永远不会被视为验证参数一样--FxCop仍会告诉你检查参数是否为null,即使这就是“保护功能”正在做。

    你基本上有两种选择。

    1. 排除问题或关闭规则。它无法实现您想要的效果。
    2. 创建一个了解扩展方法的自定义/派生规则。使用自定义规则代替默认规则。
    3. 在自己编写了自定义FxCop规则之后,我会告诉您我发现它... 非平凡。如果你确实走了这条路,虽然世界上的建议是使用新的Phoenix引擎规则样式(这是当前DisposeObjectsBeforeLosingScope使用的),我发现更容易理解旧的/标准的FxCop SDK规则(请参阅主FxCop文件夹中的FxCopSdk.dll)。反射器对于弄清楚如何做到这一点将是一个巨大的帮助,因为它上面几乎没有文档。查看Rules文件夹中的其他程序集以查看这些程序集的示例。

答案 1 :(得分:1)

我根本不是FxCop专家,但this question关于使用SuppressMessage回答它吗?我不知道用SuppressMessage属性装饰你的SafeDispose方法是否会导致FxCop在调用它的方法分析时抑制该消息,但似乎值得一试。

不要相信下面的语法,但有点像:

[SuppressMessage("Microsoft.Design", "CA2000:Dispose objects before losing scope", Justification = "We just log the exception and return an HTTP code")]
public static void SafeDispose(this IDisposable disposable)

答案 2 :(得分:0)

由于Travis概述的所有原因,此代码分析规则是一个有问题的规则。它似乎排除了任何“新”操作,除非处理呼叫接近,否则CA2000会触发。

而不是使用new,在体内调用一个方法:

MyDisposableClass result;
MyDisposableClass temp = null;
try
{
  temp = new MyDisposableClass();
  //do any initialization here
  result = temp;
  temp = null;
}
finally
{
  if (temp != null) temp.Dispose();
}
return result;

这样做是为了消除初始化导致对象无法进行处理的任何可能性。在您的情况下,当您“新建”私有成员时,您可以在看起来像上述方法的方法中进行。使用此模式后,您当然仍然负责正确处理,并且您的扩展方法是推广该空检查的好方法。

我发现你可以避免使用CA2000,同时仍然传递IDisposable并用它们做你想做的事 - 只要你在上面的方法中正确地新建它们。试一试,让我知道它是否适合你。祝你好运,好问题!

此规则的其他修补程序(包括此规则)在此处列出:CA2000: Dispose objects before losing scope (Microsoft)