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

时间:2011-05-24 14:16:08

标签: c# .net code-analysis

我在这里需要一些建议,希望有人可以帮助我。我有以下类结构(简化):

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()'方法上获得警告CA2000。此警告提供以下消息:

  

“Microsoft。可靠性:在方法'FooProvider.GetFooX()'中,在对所有引用超出范围之前,在对象'bar'上调用System.IDisposable.Dispose。”

Microsoft建议永远不要禁止此警告,但我不确定它是否会对代码中的实际问题发出警告。确实,在我们考虑的任何'CreateFooX()'方法中,'bar'在超出范围之前没有被处理掉,但是对它的引用仍存在于'FooX'对象中,最终将被处理掉,并将反过来处理'酒吧”。

我是否理解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;
       }
    }

    ...
}

但我仍然得到同样的警告。我想这只是一个误报,我可以安全地进入它。

感谢您的任何建议。

4 个答案:

答案 0 :(得分:3)

这是代码分析部分的典型误报。它真的无法理解你的代码的内在情况,因此它会给出一个通用的答案。小心谨慎,但只要您确认自己有误报,就可以放心地忽略它。

答案 1 :(得分:2)

这不是假阳性。如果在创建Bar之后但在将其传递给Foo构造函数之前抛出异常,该怎么办?我看到几个代码路径,其中可能不会丢弃一个或多个对象。

答案 2 :(得分:0)

你的一次性图案对我来说似乎有些偏差。我不认为你应该调用bar.Dispose在FooBase类中。为了您正在处理的物体的安全性,并且能够安全地呼叫多次处理,我将重新采用这种方法。

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

我希望这有用。

答案 3 :(得分:0)

这个问题的至少一部分并非真正的误报,即使它不一定是一个非常有用的问题检测。要解决您编辑的版本的剩余问题,您需要在try分配后立即打开bar块,而不是在它之前。 e.g

Bar bar = new Bar();
try
{
    ///...            
    return new FooA(bar);
}
catch
{
    bar.Dispose();
    throw;
}

不幸的是,在您进行此更改后,您仍然会受到CA2000违规,这可能是误报。这是因为规则不会检查您是否将bar置于新创建的FooA的状态中。如果它进入FooA状态,您可以安全地为违规创建抑制。但是,如果它不进入FooA中的状态,则应将其置于finally子句而不是catch子句中。