对象实例化中的异常。初始化成员会发生什么?

时间:2014-10-07 20:45:02

标签: c# .net object garbage-collection

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

但初始化字段会发生什么?他们还留在记忆中还是立即收集垃圾?如果在构造函数中发生异常之前声明并初始化了非托管资源,该怎么办?这种非托管资源会存活下来吗?

2 个答案:

答案 0 :(得分:3)

如果在构造函数中抛出异常,则类型的集合没有区别,如果没有则抛出异常。 GC运行时,如果无法从root项中访问该对象,则会清除它。如果由于初始化失败而没有引用该对象,则会在下一个集合中清除它。

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

答案 1 :(得分:1)

如果构造对象需要获取资源,那么在构造函数因任何原因抛出时防止资源泄漏的唯一方法是要求使用工厂方法来执行所有对象构造,该方法可以处理对象清理。出现问题的事件。不幸的是,.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#没有。