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

问题描述

| 我在这里需要一些建议,希望有人可以帮助我。我具有以下类结构(简化):
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在对象\'bar \'上。 Microsoft建议永远不要抑制此警告,但我不确定该警告是否涉及代码中的实际问题。没错,'bar'不会在我们考虑的任何\'CreateFooX()'方法中超出范围之前进行处理,而是对其的引用存在于\'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.Dispose在FooBase类中。为了确保您要处置的对象的安全并能够多次调用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
之后而不是之前打开before6ѭ块。例如。:
Bar bar = new Bar();
try
{
    ///...            
    return new FooA(bar);
}
catch
{
    bar.Dispose();
    throw;
}
不幸的是,进行此更改后,您仍然会遇到违反CA2000的情况,这可能是误报。那是因为规则不会检查您是否将ѭ7放入新创建的ѭ10的状态。如果它在ѭ10中进入状态,则可以安全地创建对违例的抑制。但是,如果它不在ѭ10中进入状态,则应将其放置在
finally
子句中,而不是在
catch
子句中。