通常,当您处置私有成员时,您可能会执行以下操作:
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
就其而言?
答案 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
。这是进行评估的实际班级。
查看其中的代码,您可以看到与您的问题有关的两件事。
SharedNeedsDisposedAnalysis
的共享服务。FunctionBodyRule
。第一项很有意思,因为 SharedNeedsDisposedAnalysis
决定了哪些符号需要Dispose()
调用。这是非常彻底的,通过代码“行走”来确定需要什么被处置和实际处置的东西。然后它会保留这些内容的表格供以后使用。
第二项很有意思,因为 FunctionBodyRule
规则会评估单个函数的正文。还有其他规则类型,例如FunctionCallRule
,用于评估函数调用成员之类的内容(例如ProvideCorrectArgumentsToFormattingMethods
)。
关键是,SharedNeedsDisposedAnalysis
服务中潜在的“未命中”,它可能不会通过您的方法递归,以确定事情实际上已被处理,FunctionBodyRule
的限制不会超出功能正文,它只是没有捕捉你的扩展名。
这就像Guard.Against<ArgumentNullException>(arg)
之类的“保护函数”在使用之前永远不会被视为验证参数一样--FxCop仍会告诉你检查参数是否为null,即使这就是“保护功能”正在做。
你基本上有两种选择。
在自己编写了自定义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)