代码分析友好的方式来处理对象
作为 Visual Studio 2010(主要是 C# 4.0)开发标准的一部分,我们启用了代码分析。当我审查最近提交的新项目代码时,我看到大量
CA2000:Microsoft.Reliability:在方法“XYZ”中,对象“ABC”不是 沿着所有异常路径进行处理。调用 System.IDisposable.Dispose 对象“ABC”,然后对它的所有引用都超出范围。
警告。问题是,我所做的一切似乎都无法消除这些警告——而且我花了几个小时在网上搜索并尝试了一切可能的方法。
首先,让我明确一点,我并不是在谈论放置一个简单的 using 块来正确处理局部变量 - 这不是问题。就我而言,当对象由方法返回或分配给方法内的另一个对象时,就会出现这些警告。
下面是包含四个此类警告的代码示例:
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。
另外,虽然对象初始值设定项可能是问题的一个组成部分,但请记住,初始值设定项与两个私有方法隔离,并且与 MainMethod 中的警告无关。
谁能告诉我如何正确实施这些方法来消除 CA2000 警告?
As part of our Visual Studio 2010 (primarly C# 4.0) development standards, we have Code Analysis turned on. As I am reviewing recently submitted code for a new project, I am seeing a ton of
CA2000 : Microsoft.Reliability: In method 'XYZ', object 'ABC' is not
disposed along all exception paths. Call System.IDisposable.Dispose on
object 'ABC' before all references to it are out of scope.
warnings. The problem is that nothing I do seems to eliminate the warnings - and I've spent hours scouring the web and trying everything I can.
First, let me be clear that I am not talking about putting a simple using block in to properly dispose of a local variable - that's not an issue. In my case, these warnings are appearing when the object is either returned by the method or assigned to another object within the method.
Here is a code sample that contains four such warnings:
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;
}
I've commented the lines where the warnings occur.
I've tried refactoring both Create methods as described in the MSDN article (here) but the warnings still appear.
UPDATE
I should note that both SomeObject and SomeOtherObject implement IDisposable.
Also, while object initializers may be a component of the problem, keep in mind that the initializers are isolated to the two private methods and have nothing to do with the warnings in MainMethod.
Can anyone show me how to properly implement these methods to eliminate the CA2000 warnings?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在这种情况下,CA2000 检测到的问题是,如果在将一次性实例传递出方法之前发生异常,则一次性实例可能会“孤立”。例如,CreateFirstObject 的“正确”实现将如下所示:
鉴于您所描述的有关 MainMethod 所需行为的内容,其“正确”实现可能如下所示:
The problem that is being detected by CA2000 in this case is that a disposable instance may be "orphaned" if an exception occurs before it is passed out of the method. For example, a "correct" implementation of CreateFirstObject would look something like the following:
Given what you have described concerning your desired behaviour for MainMethod, its "correct" implementation might look something like this:
消除警告的一种方法是在代码中抑制它:
但这并不是问题的真正解决方案。
此处描述了解决方案:如何所有权转移时摆脱 CA2000 警告?
在提到的链接中,基本上说明了将对象添加到实现的集合中
ICollection
,但我还没有测试过。One way to get rid of the warning is to suppress it in code:
But this is not a real solution of the problem.
A solution is described here: How to get rid of CA2000 warning when ownership is transferred?
In the mentioned link it is basically stated to add the object to a collection implementing
ICollection<T>
, but I haven't tested that.如果将 main 中返回的对象包装在 using 块中,或者实现finally 来处理这些对象,会发生什么?
SomeOtherObjects 是否需要实现 IDisposable?
What happens if you wrap the returned objects in main in a using block, or implement a finally to dispose of the objects?
Do the SomeOtherObjects need to implement IDisposable?
需要的是实现类似于“using”块的模式,但在成功返回对象的情况下禁用对象的处置。 Nicole Calinoiu 提供的方法是合理的,尽管我更喜欢避免捕获只会冒泡的异常。考虑到 C# 语言的限制,我首选的代码表达是使用 InitializedSuccessively 标志,然后有一个“finally”块,负责在尚未调用 InitializedSuccessively 时进行处理。
如果一个类将包含许多 IDisposable 对象,并且一旦构造完成,这些对象的集合将被固定,那么定义一个 IDisposable 管理器类来保存 IDisposable 对象的列表可能会很有用。让类的构造函数接受 DisposableManager 对象作为参数,并将其构造的所有对象放入由此生成的列表中(对于您的类来说,拥有一个实例方法可能会有所帮助:
要在 myDisposableManager 初始化之后使用它,简单地说:
var someDisposableField = RegDispose(new someDisposableType());
这样的模式将提供两大好处:在 vb 中,基类构造函数可以将构造函数参数公开为可供字段初始值设定项使用的字段。因此,我们可以在字段初始值设定项以及显式构造函数中很好地使用 RegDispose 模式。在 C# 中这是不可能的。可以使用 [threadstatic] 字段来达到此目的,但需要注意一些以确保设置的任何此类字段也会被取消设置。从线程池线程之类的内部调用构造函数可能会产生内存泄漏。此外,线程静态字段的访问效率几乎无法与普通字段一样高,而且我不知道 C# 中有什么方法可以避免必须多次重新获取线程静态字段——每个注册的 IDisposable 对象一次。
What is needed is to implement a pattern similar to a "using" block, but disabling the disposal of the object in the scenario where it will be returned successfully. The approach offered by Nicole Calinoiu is reasonable, though I prefer to avoid catching exceptions that are simply going to bubble up. My preferred expression of the code, given the constraints of the C# language, would be to use an InitializedSuccessfully flag, and then have a "finally" block which takes care of the disposal if InitializedSuccessfully has not been called.
If a class is going to contain many IDIsposable objects, and the set of such objects will be fixed once construction is complete, it may be useful to define an IDisposable manager class which will hold a list of IDisposable objects. Have the constructors of your class accept a DisposableManager object as a parameter, and place all of the objects it constructs into the list generated thereby (it may be helpful for your class to have an instance method:
To use it, after myDisposableManager had been initialized, simply say something like:
var someDisposableField = RegDispose(new someDisposableType());
. Such a pattern would offer two big benefits:In vb, it's possible for a base-class constructor to expose a constructor parameter as a field which is available to field initializers. Thus, one could nicely use the RegDispose pattern in field initializers as well as in the explicit constructor. In C# that is not possible. It is possible to use [threadstatic] fields for that purpose, but some caution would be required to ensure that any such fields that get set also get unset. A constructor gets called from within something like a threadpool thread could generate a memory leak. Also, threadstatic fields cannot be accessed nearly as efficiently as normal ones, and I don't know any way in C# to avoid having to re-fetch the thread-static field many times--once for each IDisposable object registered.
我们使用的模式大多数时候会清除警告,
不幸的是,仍然存在某些情况下警告不会消失,因此我们会在本地删除警告,并给出理由说一次性用品已被覆盖有图案。
微软文档。
The pattern we use and that clears the warning most of the time is
Unfortunately, there are still some cases where the warning won't go so we then delete the warning locally with a justification saying the disposable is covered with pattern.
This is very briefly mentioned on Microsoft Documentation.