【问题标题】:Exception in object instantiation. What happens to initialized members?对象实例化异常。初始化的成员会发生什么?
【发布时间】:2014-10-07 20:45:01
【问题描述】:

根据this 文章,在执行构造函数之前初始化字段。但是,如果在构造函数中抛出异常怎么办?对象实例化将失败。

但是初始化的字段会发生什么?它们仍然保留在内存中还是立即被垃圾收集?如果在构造函数中的异常发生之前声明并初始化了非托管资源怎么办。这种非托管资源会继续存在吗?

【问题讨论】:

  • 你用调试器试过了吗?也有关系吗?
  • 没有尝试使用调试器。当我们处理非托管资源时,这很重要。

标签: c# .net object garbage-collection


【解决方案1】:

如果在构造函数中抛出异常,则类型的集合与没有异常时的集合没有什么不同。当 GC 运行时,如果无法从根项目访问该对象,它将被清理。如果由于初始化失败而没有对该对象的引用,它将在下一次收集时被清除。

非托管资源不会自行清理。这实际上是非托管资源的定义。非托管资源是不能自行清理的任何东西;托管资源是自行清理的资源。在处理非托管资源时,您需要支持类型初始化失败的情况并适当地清理资源,如果不这样做,那么您已经泄漏了它们,并且需要处理任何后果。

【讨论】:

  • 所以让我重申一下。您的意思是,一旦抛出异常,失败的对象初始化将使该类的所有字段都有资格进行垃圾收集。虽然必须明确清理非托管资源 - 不确定我们如何实现这一点;假设我已经实现了 IDisposable 并重写了 Finalize(),应该在哪里调用清理?
  • @Lucifer 一个聪明的开发者会完全避免这种情况;不要使用可能抛出持有非托管资源的类型的字段初始化器;使用可以捕获异常并在重新抛出之前优雅地自行关闭的构造函数。
【解决方案2】:

如果构造一个对象需要获取资源,那么在构造函数因任何原因抛出异常时防止资源泄漏的唯一方法是要求所有对象构造都使用可以处理对象清理的工厂方法执行出现问题的事件。不幸的是,.NET 没有做任何事情来方便这一点。一种方法是:

static public MyThing Create(...)
{
  var cleanupList = new List<IDisposable>();
  try
  {
    MyThing Result = new MyThing(cleanupList, ...); // private or protected constructor
  }
  finally
  {
    if (Result == null)
    {
      List<Exception> failureList = null;
      foreach (IDisposable cleaner in cleanupList)
      {
        try
        {
          cleaner.Dispose();
        }
        catch(Exception ex)
        {
          if (failureList == null)
            failureList = new List<Exception>();
          failureList.Add(ex);
        }
      }
      if (failureList != null)
        throw new FailedConstructorCleanupException(failureList);
    }
  }      
}

如果在执行Dispose 操作时失败,FailedConstructorCleanupException 确实应该封装来自抛出的构造函数的异常,但是当清理成功时,构造函数失败异常应该通过包装器而不会被捕获并重新抛出。不幸的是,虽然 VB.NET 可以让 Finally 块知道在 Try 中抛出了什么异常,而无需捕获并重新抛出,但 C# 却没有。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    相关资源
    最近更新 更多