14

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

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

本地分配的目的是避免在空检查之后另一个线程可能将私有成员分配为空的竞争条件。在这种情况下,我不在乎是否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:在每种情况下都失去范围警告之前处理对象。

我不想关闭这条规则,也不想压制每一个案例。有没有办法向 FxCop 暗示这种方法就Dispose它而言是等效的?

4

3 回答 3

9

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

一点点 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.

第一项很有趣,因为它决定了需要调用SharedNeedsDisposedAnalysis哪些符号。Dispose()它非常彻底,通过代码“遍历”以确定需要处理的内容以及实际处理的内容。然后它会保留这些东西的表格以供以后使用。

第二项很有趣,因为FunctionBodyRule规则会评估单个函数的主体。还有其他规则类型,例如FunctionCallRule评估函数调用成员(例如,ProvideCorrectArgumentsToFormattingMethods)之类的东西。

关键是,在该SharedNeedsDisposedAnalysis服务中潜在的“遗漏”之间,它可能不会通过您的方法递归查看事情实际上正在被处理,以及FunctionBodyRule不超出函数体的限制,它只是没有抓住您的扩展。

这与“保护函数”之类的“保护函数”Guard.Against<ArgumentNullException>(arg)永远不会被视为在您使用它之前验证参数的原因相同 - FxCop 仍然会告诉您检查参数是否为空,即使这就是“保护函数”正在做的事情。

你基本上有两个选择。

  1. 排除问题或关闭规则。它不可能做你想做的事。
  2. 创建将理解扩展方法的自定义/派生规则。使用您的自定义规则代替默认规则。

在自己编写了自定义 FxCop 规则后,我会告诉你我发现了它......非常重要。如果你确实走这条路,虽然世界上的建议是使用新的 Phoenix 引擎规则样式(这是当前DisposeObjectsBeforeLosingScope使用的),但我发现更容易理解旧的/标准的 FxCop SDK 规则(见FxCopSdk.dll主FxCop 文件夹)。Reflector 将极大地帮助您弄清楚如何做到这一点,因为它上面几乎没有零文档。查看文件夹中的其他程序集Rules以查看这些程序集的示例。

于 2012-06-28T17:46:40.333 回答
1

我根本不是 FxCop 专家,但是这个关于使用 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)
于 2012-06-28T17:13:56.177 回答
0

由于 Travis 概述的所有原因,此代码分析规则是一个有问题的规则。它似乎将任何“新”操作排入队列,除非 dispose 调用关闭,否则 CA2000 会触发。

不要使用 new,而是在主体中调用带有 this 的方法:

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;

这样做是为了消除初始化导致对象无法处理的任何可能性。在您的情况下,当您“新建”私人成员时,您将在类似于上述方法的方法中执行此操作。使用此模式后,您当然仍然负责正确处理,并且您的扩展方法是概括该 null 检查的好方法。

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

此处概述了此规则的其他修复(包括此规则):CA2000:在失去范围之前处置对象(Microsoft)

于 2012-06-28T18:15:09.483 回答