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;
       }
    }
    ...
}

但我仍然收到同样的警告。我想这只是一个假阳性,我很安全。

谢谢你的建议。

VS2010代码分析期间的IDisposable和CA2000警告

这是代码分析部分的典型误报。它真的无法理解代码的内在情况,所以它给出了一个一般的答案。请谨慎操作,但无论何时验证您有假阳性,您都可以安全地忽略它。

这不是假阳性。如果在创建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子句中处理。