【问题标题】:Throwing exceptions from a constructor in .NET从 .NET 中的构造函数中抛出异常
【发布时间】:2010-10-29 21:47:34
【问题描述】:

当我从如下构造函数中抛出异常时是否存在内存泄漏?

class Victim
{
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj";

    public Victim()
    {
        //throw new Exception("oops!");
    }
}

垃圾收集器会收集失败的对象吗?

【问题讨论】:

  • 几乎不相关但有用的提示:小心在控件的构造函数中抛出的异常。它可以破坏控件/表单的设计器。我通过使用 Initialise() 方法并在外部调用它来解决它(但我不喜欢它)。

标签: .net exception memory garbage-collection


【解决方案1】:

一般来说,从不泄漏内存的角度来看,这是安全的。但是,如果您在类型中分配非托管资源,则从构造函数中抛出异常是危险的。举个例子

public class Foo : IDisposable { 
  private IntPtr m_ptr;
  public Foo() {
    m_ptr = Marshal.AllocHGlobal(42);
    throw new Exception();
  }
  // Most of Idisposable implementation ommitted for brevity
  public void Dispose() {
    Marshal.FreeHGlobal(m_ptr);
  }
}

即使您使用 using 块,每次尝试创建此类都会泄漏内存。例如,这会泄漏内存。

using ( var f = new Foo() ) {
  // Won't execute and Foo.Dispose is not called
} 

【讨论】:

  • 需要明确的是,不仅using语句的主体不执行……而且Foo的.Dispose()方法也没有被调用,因为没有创建实例来调用.Dispose() on.
  • 我在这里聚会真的迟到了,但是构造函数中的 try-finally 不会解决这个问题吗?
【解决方案2】:

如果您没有创建非托管资源,那么从构造函数中抛出异常应该没问题。但是,如果您确实在构造函数中创建了非托管资源,则该构造函数的整个主体,包括 throws,都应该包装在 try/catch 中。借鉴 JaredPar 的优秀范例:

public class Foo : IDisposable { 
  private IntPtr m_ptr;
  public Foo() {
    try
    {
        m_ptr = Marshal.AllocHGlobal(42);
        throw new Exception();
    }
    catch
    {
        Dispose();
        throw;
    }
  }
  // Most of Idisposable implementation ommitted for brevity
  public void Dispose() {
    Marshal.FreeHGlobal(m_ptr);
  }
}

现在可以使用以下方法:

using ( var f = new Foo() ) {
  // Won't execute, but Foo still cleans itself up
}

【讨论】:

    【解决方案3】:

    很有趣,因为我昨天帮助了similar question

    如果您有派生类型,这是一个更大的问题,因为派生类型的某些部分会初始化,而其他部分则不会。从内存的角度来看,这并不重要,因为垃圾收集器知道什么在哪里。但是,如果您有任何非托管资源(实现 IDisposable),事情就会变得模糊不清。

    【讨论】:

      【解决方案4】:

      这取决于您在引发异常之前获得了哪些其他资源。我不认为在构造函数中抛出异常很好,但在终结器或 dispose() 中抛出异常要糟糕得多。

      【讨论】:

      • “将它们扔到终结器或 dispose() 中要糟糕得多。”。在 Dispose 中抛出是可以接受的 - 例如,如果 FileStream 无法刷新 Stream(例如由于网络错误),它将抛出。如果可以,请避免投掷,但如果必须这样做。在 FileStream 的情况下,吞下异常比抛出要危险得多,因为它会给调用者一种文件已成功写入的印象。
      【解决方案5】:

      是的,垃圾收集器将回收已在对象中分配的托管资源。如果您已初始化任何非托管资源,则需要以正常方式自行清理这些资源。

      【讨论】:

        猜你喜欢
        • 2019-03-11
        • 2011-11-04
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-03
        相关资源
        最近更新 更多