对象初始值设定项和属性可以抛出异常时的Dispose

时间:2011-06-20 10:37:15

标签: c#

我有以下设置:

public class SomeClass
{
  private DirectoryEntry _root;
  private DirectorySearcher _searcher;

  public SomeClass()
  {
    _root = new DirectoryEntry("ldap://bla");
    _searcher = new DirectorySearcher(_root)
      {
        PageSize = int.MaxValue,
        SizeLimit = int.MaxValue
      }
  }
}

我使用int.MaxValue的原因是因为在这种情况下我知道我会超过默认值,但数字永远不会太大,所以我对此很好。

但如果我打开代码分析 Microsoft基本正确性规则,它会抱怨:

  

警告2 CA2000:Microsoft.Reliability:在方法'SomeClass.SomeClass()'中,对象'<> g_ initLocal0'未沿所有异常路径放置。在对对象'<> g _initLocal0'调用System.IDisposable.Dispose之前,对它的所有引用都超出了范围。

问题是PageSize和SizeLimit可以抛出异常,如果发生这种情况,G__initLocal0对象就不会被处理掉(即使_searcher被处置掉了)。他们可以抛出的例外情况是,如果你将它们分配给负数,这在这里不会发生,但它仍然会抱怨。

接下来,我使用常规赋值语句在对象intitializer外部设置属性,但是然后ReSharper抱怨告诉我应该使用Initializer。我可以压制ReSharper,但我喜欢想办法让事情发挥作用,而不会增加抑制。

所以我想我必须抓住错误,如果可能的话,我不喜欢我的构造函数中的catch错误,所以我创建了一个名为Searcher的属性:

private DirectorySearcher _searcher;
public DirectorySearcher Searcher
{
  get
  {
    if (_searcher != null) return _searcher;
    var searcher = new DirectorySearcher();
    try
    {
      searcher.PageSize = int.MaxValue;
      searcher.SizeLimit = int.MaxValue;
      _searcher = searcher;
    }
    catch
    {
      searcher.PageSize = 1000;
      searcher.SizeLimit = 1000;
    }
    finally
    {
      searcher.Dispose();
    }
    return _searcher;
  }
}

现在代码分析,一切都很开心,但我对解决方案一点都不满意。

任何提示?

3 个答案:

答案 0 :(得分:7)

问题是编译器正在有效地生成:

public class SomeClass
{
  private DirectoryEntry _root;
  private DirectorySearcher _searcher;

  public SomeClass()
  {
    _root = new DirectoryEntry("ldap://bla");

    var temp = new DirectorySearcher(_root);
    temp.PageSize = int.MaxValue;
    temp.SizeLimit = int.MaxValue;

    _searcher = temp;
  }
}

您可以通过不为_searcher使用较新的属性初始化程序语法来避免这种情况,以便在设置属性之前确保将其正确分配给字段,请参阅Object initializers in using-block generates code analysis warning CA2000

这里存在第二个问题,即如果在构造SomeClass期间出现错误,则调用代码将无法处置SomeClass,因此无法处置_root }或_searcher,请参阅Handling IDisposable in failed initializer or constructor

答案 1 :(得分:4)

现在你做事的方式可能会让每个人都开心,但你却无法工作。您将在DirectorySearcher媒体资源中退回已处置的Searcher

我只是这样做:

public SomeClass()
{
    _root = new DirectoryEntry("ldap://bla");

    try
    {
        _searcher = new DirectorySearcher(_root);
        _searcher.PageSize = 1000;
        _searcher.SizeLimit = 1000;
    }
    catch
    {
         if (_searcher != null)
         {
             _searcher.Dispose();
         }

         throw;
    }

}

我没有看到在构造函数中使用try-catch块有什么问题。

在构造IDisposable对象时,我不建议使用属性初始化程序语法,因为如果属性初始化抛出,则无法正常处理它们。

答案 2 :(得分:0)

如果在SomeClass实例的整个生命周期中需要_searcher,SomeClass应该实现IDisposable并在Dispose中处理_searcher。

请参阅http://msdn.microsoft.com/de-de/library/system.idisposable.aspx