VS2010代码分析期间的IDisposable和CA2000警告
本文关键字:IDisposable CA2000 警告 代码 VS2010 | 更新日期: 2023-09-27 17:57:44
我需要一些建议,我希望有人能帮助我。我有以下类结构(简化):
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类的所有"CreateFooX()"方法上得到Warnings CA2000。此警告给出以下信息:
Microsoft.Reliability:在方法"FooProvider.GetFooX()"中,在对对象"bar"的所有引用都超出作用域之前,调用对象上的System.IDisposable.Dispose
微软建议永远不要取消此警告,但我真的不确定它对代码中真正问题的警告。的确,在我们考虑的任何"CreateFooX()"方法中,"bar"在超出范围之前都不会被释放,但对它的引用仍存在于"FooX"对象中,该对象最终将被释放,并将负责处理"bar"。
我是否理解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
构造函数之前抛出异常,该怎么办?我看到了几个代码路径,其中一个或多个对象可能没有被处理。
我觉得你的一次性模式有点不对劲。我认为你不应该打电话给bar。在FooBase类中进行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;
}
}
}
至于错误,我认为这应该注意静态分析警告。我在一个测试项目中实现了如下代码,启用了所有静态分析警告,没有出现警告问题。
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
中进入状态,则应该在finally
子句中处理它,而不是在catch
子句中处理。