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

问题描述:

根据this文章,字段在执行构造函数之前被初始化。但是,如果异常抛出构造函数呢?对象实例化将失败。对象实例化中的异常。初始化成员会发生什么?

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

+0

你用调试器试过了吗?这也有关系吗? – gunr2171 2014-10-07 20:49:10

+0

没有尝试过使用调试器。当我们处理非托管资源时它很重要。 – Lucifer 2014-10-07 21:09:09

如果在构造函数中引发异常,那么类型的集合没有区别,如果没有。 GC运行时,如果对象无法从根目录项访问,则会清除该对象。如果由于其初始化失败而没有对该对象的引用,它将在下一次收集时清除。

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

+1

所以让我重申一遍。你的意思是失败的对象初始化会在抛出异常时让该类的所有字段符合垃圾收集的条件。虽然非托管资源必须明确清理 - 不确定我们如何实现这一目标;假设我已经实现了IDisposable并重写了Finalize(),那么应该在哪里调用清理? – Lucifer 2014-10-07 21:06:32

+0

@Lucifer一位聪明的开发人员完全可以避免这种情况;不要使用可能抛出持有非托管资源的类型的字段初始值设定项;使用一个构造函数可以捕获异常并在重新抛出之前优雅地将自己撕掉。 – Servy 2014-10-08 13:49:10

如果构建一个对象需要获取资源,如果构造函数因任何原因抛出而阻止资源泄漏的唯一方法是要求使用可以处理对象清理的工厂方法来执行所有对象构造在出现错误的情况下。不幸的是,.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#不会。