![](/img/trans.png)
[英]VS2010: Code analysis warning CA2000. Object is not disposed along all exception paths
[英]IDisposable and CA2000 warning during VS2010 Code Analysis
我在这里需要一些建议,我希望有人可以帮助我。 我有以下 class 结构(简化):
public class Bar: IDisposable {...}
public abstract class FooBase: IDisposable
{
Bar bar;
bool disposed;
internal FooBase(Bar bar)
{
this.bar=bar;
}
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.bar.Dispose();
}
this.disposed = true;
}
}
}
public FooA: Foo {...}
public FooB: Foo {...}
public static class FooProvider
{
public static FooA GetFooA()
{
Bar bar = new Bar();
...
return new FooA(bar);
}
public static FooB GetFooB()
{
Bar bar = new Bar();
...
return new FooB(bar);
}
...
}
当我对此运行代码分析时,我在 FooProvider class 的所有“CreateFooX()”方法上收到警告 CA2000。 此警告提供以下消息:
“微软。可靠性:在方法 'FooProvider.GetFooX()' 中,在 scope 的所有引用之前调用 object 'bar' 上的 System.IDisposable.Dispose。”
Microsoft 建议永远不要抑制此警告,但我不确定它是否会警告代码中的实际问题。 确实,在我们考虑的任何 'CreateFooX()' 方法中,在离开 scope 之前,'bar' 都不会被处理,但对它的引用仍然存在于 'FooX' object 中,最终将被处理并反过来处理处理 '酒吧'。
我是否对 Dispose 模式的工作方式理解有误,并且我的代码中有一些根本缺陷,还是应该直接取消此警告?
编辑
由于一些评论,我尝试将工厂方法修改为以下内容:
public static class FooProvider
{
public static FooA GetFooA()
{
Bar bar = null;
try
{
bar = new Bar();
...
return new FooA(bar);
}
catch
{
if (bar != null) bar.Dispose();
throw;
}
}
...
}
但我仍然收到同样的警告。 我想这只是一个误报,我可以安全地接受它。
感谢您的任何建议。
这是代码分析部分的典型误报。 它真的无法理解你的代码的内在情况,所以它给出了一个普遍的答案。 请谨慎行事,但只要您确认您有误报,您就可以放心地忽略它。
这不是误报。 如果在创建Bar
之后但在将其传递给Foo
构造函数之前抛出异常怎么办? 我看到几个代码路径可能不会处理一个或多个对象。
你的一次性模式对我来说似乎有点不对劲。 我认为您不应该在 FooBase class 中调用 bar.Dispose。 为了您正在处理的对象的安全,并且能够多次安全地调用 Dispose,我会推荐这种方法。
private bool _disposed;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if ( disposing )
{
if ( !_disposed )
{
if ( Bar != null )
{
Bar.Dispose();
}
_disposed = true;
}
}
}
至于错误,我认为这应该照顾到 static 分析警告。 我在一个测试项目中按如下方式实现了您的代码,启用了所有 static 分析警告,而没有出现警告问题。
public class Bar : IDisposable
{
private bool _disposed;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if ( disposing )
{
if ( !_disposed )
{
_disposed = true;
}
}
}
}
public abstract class FooBase : IDisposable
{
public Bar Bar
{
get;
set;
}
internal FooBase( Bar bar )
{
Bar = bar;
}
private bool _disposed;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if ( disposing )
{
if ( !_disposed )
{
if ( Bar != null )
{
Bar.Dispose();
}
_disposed = true;
}
}
}
}
public class FooA : FooBase
{
public FooA( Bar bar )
: base( bar )
{
}
}
public static class FooProvider
{
public static FooA GetFooA()
{
Bar bar;
using ( bar = new Bar() )
{
return new FooA( bar );
}
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void StaticAnalysisTest()
{
Assert.IsNotNull( FooProvider.GetFooA().Bar );
}
}
我希望这是有帮助的。
至少这个问题的一部分并不是真正的误报,即使它不一定是一个非常有用的问题检测。 要解决您编辑版本的剩余问题,您需要在bar
分配之后立即打开try
块,而不是在它之前。 例如:
Bar bar = new Bar();
try
{
///...
return new FooA(bar);
}
catch
{
bar.Dispose();
throw;
}
不幸的是,在您进行此更改后,您仍然会遇到 CA2000 违规,这可能是误报。 这是因为该规则不会检查您是否将bar
放入新创建的 FooA 的FooA
中。 如果它进入 FooA 中的FooA
,您可以安全地为违规创建抑制。 但是,如果它没有进入 FooA 中的FooA
,则应将其放置在finally
子句而不是catch
子句中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.