C# 代码分析友好的对象处理方法
作为VisualStudio2010(主要是C#4.0)开发标准的一部分,我们启用了代码分析。当我审查最近提交的一个新项目的代码时,我看到了大量的错误 CA2000:Microsoft。可靠性:在方法“XYZ”中,对象“ABC”不是 沿所有异常路径处置。在上调用System.IDisposable.Dispose 对象“ABC”之前,所有对它的引用都超出范围 警告。问题是,我所做的一切似乎都无法消除这些警告——我花了数小时浏览网页,尽我所能尝试一切 首先,让我明确一点,我不是在讨论如何使用一个简单的using块来正确地处理局部变量——这不是一个问题。在我的例子中,当对象由方法返回或分配给方法中的另一个对象时,会出现这些警告 以下是包含四个此类警告的代码示例:C# 代码分析友好的对象处理方法,c#,.net,code-analysis,idisposable,static-code-analysis,C#,.net,Code Analysis,Idisposable,Static Code Analysis,作为VisualStudio2010(主要是C#4.0)开发标准的一部分,我们启用了代码分析。当我审查最近提交的一个新项目的代码时,我看到了大量的错误 CA2000:Microsoft。可靠性:在方法“XYZ”中,对象“ABC”不是 沿所有异常路径处置。在上调用System.IDisposable.Dispose 对象“ABC”之前,所有对它的引用都超出范围 警告。问题是,我所做的一切似乎都无法消除这些警告——我花了数小时浏览网页,尽我所能尝试一切 首先,让我明确一点,我不是在讨论如何使用一个简
public void MainMethod()
{
var object1 = CreateFirstObject(); // Warning here
var object2 = CreateSecondObject(); // Warning here
SomeCollectionProperty.Add(object1);
SomeCollectionProperty.Add(object2);
}
private SomeObject CreateFirstObject()
{
var theObject = new SomeObject() // Warning here
{
FirstProperty = "some value",
// ...
};
return theObject;
}
private SomeOtherObject CreateSecondObject()
{
var theObject = new SomeOtherObject() // Warning here
{
FirstProperty = "a different value",
// ...
};
return theObject;
}
我已经对出现警告的行进行了注释
我已经尝试过按照MSDN文章()中的描述重构这两个Create方法,但仍然会出现警告
更新
我应该注意,SomeObject和SomeOtherObject都实现了IDisposable
此外,虽然对象初始值设定项可能是问题的一个组成部分,但请记住,初始值设定项与两个私有方法隔离,与main方法中的警告无关
有人能告诉我如何正确地实现这些方法以消除CA2000警告吗?如果将返回的对象主要包装在using块中,或者实现最终处理对象,会发生什么情况
SomeOther对象是否需要实现IDisposable?消除警告的一种方法是在代码中抑制它:
[SuppressMessage(
"Microsoft.Reliability",
"CA2000:DisposeObjectsBeforeLosingScope",
Justification = "Factory method")]
但这并不是真正的问题解决方案
这里描述了一种解决方案:
在上面提到的链接中,基本上说明了将对象添加到实现ICollection的集合中,但我还没有对此进行测试。在这种情况下,CA2000检测到的问题是,如果在传递出方法之前发生异常,则一次性实例可能是“孤立的”。例如,CreateFirstObject的“正确”实现如下所示:
private SomeObject CreateFirstObject()
{
var theObject = new SomeObject();
try
{
theObject.FirstProperty = "some value";
}
catch
{
theObject.Dispose();
throw;
}
return theObject;
}
考虑到您对MainMethod的期望行为的描述,其“正确”实现可能如下所示:
public void MainMethod()
{
var object1 = CreateFirstObject();
try
{
SomeCollectionProperty.Add(object1);
var object2 = CreateSecondObject();
try
{
SomeCollectionProperty.Add(object2);
}
catch
{
object2.Dispose();
throw;
}
}
catch
{
object1.Dispose();
SomeCollectionProperty.Remove(object1); // Not supposed to throw if item does not exist in collection.
throw;
}
}
所需要的是实现一个类似于“使用”块的模式,但在成功返回对象的场景中禁用对该对象的处理。Nicole Calinoiu所提供的方法是合理的,尽管我更愿意避免捕捉那些只会冒泡的异常。考虑到C#语言的限制,我更喜欢的代码表达式是使用InitializedSuccessfully标志,然后有一个“finally”块,如果没有调用InitializedSuccessfully,它负责处理 如果一个类将包含许多IDIsposable对象,并且一旦构造完成,这些对象的集合将被修复,那么定义一个IDIsposable管理器类可能会很有用,该类将保存IDIsposable对象的列表。让类的构造函数接受DisposableManager对象作为参数,并将其构造的所有对象放入由此生成的列表中(类具有实例方法可能会有所帮助: T regDisposable<T>RegDispose(T newThing) where T:IDisposable { myDisposableManager.Add(newThing); return newThing; } T regDisposableRegDispose(T newThing),其中T:IDisposable { myDisposableManager.Add(newThing); 返回新事物; } 要使用它,在myDisposableManager初始化之后,只需说这样的话:
var someDisposableField=RegDispose(new someDisposableType());
。这样的模式有两大好处:
在vb中,基类构造函数可以将构造函数参数公开为字段初始值设定项可用的字段。因此,可以在字段初始值设定项以及显式构造函数中很好地使用RegDispose模式。在C中,这是不可能的。可以使用[threadstatic]字段,但需要注意确保设置的任何此类字段也未设置。从线程池线程之类的内部调用构造函数可能会产生内存泄漏。此外,threadstatic字段的访问效率无法接近正常字段,我不知道在C#中如何避免必须多次重新获取线程静态字段——对于注册的每个IDisposable对象一次。我们使用的模式,大多数情况下清除警告的模式是 不幸的是,仍有一些情况下警告不会出现,因此我们随后在本地删除警告,并给出理由,说明一次性塑料覆盖了图案
这是非常简短地提到过的。我知道这并没有真正的帮助,但是……我发现CA2000产生了太多善意的误报,以至于偶尔真正的问题变得非常难以发现。出于这个原因,我通常会将其完全抑制。我不明白为什么它会产生所有这些警告。除非有人和某些其他对象正在使用非托管资源和/或具有打开的文件句柄(例如),此警告在何处不是必需的。我知道这对您的问题没有帮助,但如果
DisposableObject retVal;
DisposableObject tempVal;
try{
tempVal = new DisposableObject();
tempVal.DoStuff();
retVal = tempVal;
tempVal = null;
} finally{
if (tempVal != null) { tempVal.Dispose();} //could also be writtent tempVal?.Dispose();
}
return retVal;