2014-10-07 59 views
3

根據this文章,字段在執行構造函數之前被初始化。但是,如果異常拋出構造函數呢?對象實例化將失敗。對象實例化中的異常。初始化成員會發生什麼?

但是,初始化字段會發生什麼?他們仍然留在記憶裏,還是立即收集垃圾?如果在構造函數中發生異常之前聲明並初始化了非託管資源,該怎麼辦?這個非託管資源會存活嗎?

+0

你用調試器試過了嗎?這也有關係嗎? – gunr2171 2014-10-07 20:49:10

+0

沒有嘗試過使用調試器。當我們處理非託管資源時它很重要。 – Lucifer 2014-10-07 21:09:09

回答

3

如果在構造函數中引發異常,那麼類型的集合沒有區別,如果沒有。 GC運行時,如果對象無法從根目錄項訪問,則會清除該對象。如果由於其初始化失敗而沒有對該對象的引用,它將在下一次收集時清除。

非託管資源不會自行清理。這實際上是非託管資源的定義。非託管資源是任何不能自行清理的資源;管理資源是自己清理的資源。在處理非託管資源時,您需要支持初始化類型失敗的情況並適當地清理資源,如果不這樣做,那麼您已經泄露了它們,並且需要處理這些後果。

+1

所以讓我重申一遍。你的意思是失敗的對象初始化會在拋出異常時讓該類的所有字段符合垃圾收集的條件。雖然非託管資源必須明確清理 - 不確定我們如何實現這一目標;假設我已經實現了IDisposable並重寫了Finalize(),那麼應該在哪裏調用清理? – Lucifer 2014-10-07 21:06:32

+0

@Lucifer一位聰明的開發人員完全可以避免這種情況;不要使用可能拋出持有非託管資源的類型的字段初始值設定項;使用一個構造函數可以捕獲異常並在重新拋出之前優雅地將自己撕掉。 – Servy 2014-10-08 13:49:10

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#不會。