2011-11-14 36 views
2

我知道c#中析構函數和終結符的含義和用法的區別。c#析構函數:處理「模式」和最佳實踐

但是,典型地,「不應該使用析構函數,而是使用MSDN中顯示的配置模式」回答「我應該......」。 Eric Lippert writes相當於strongly反對不必要地使用析構函數。

但是,該「模式」主張編寫一個析構函數,如~T() { Dispose(false); }。陳述的原因是,這是一個「後備」,如果程序員忘記調用Dispose(),則稱爲「後備」。當然,這忽略了終審員在其操作中不確定的事實,甚至可能永遠不會運行。

因此:

  1. 如果我使用的Dispose模式,是否也應該提供析構函數?順便說一句,我只處理管理資源(例如實體框架DataContext)。

  2. 如果我確實提供了一個析構函數:如果我的類是從一個IDisposable派生的,它可能已經提供了一個析構函數,那麼我應該提供一個析構函數嗎?我認爲在這種情況下從來沒有寫過析構函數,但是文檔說它會自動調用基類的析構函數。

+1

有幾十個類似的問題已經在SO。 –

+4

你有沒有_unmanaged_資源開始? –

+0

不,有關於這個或那個的問題。我在問爲什麼這些答案中有一個catch-22。 –

回答

11

如果我使用dispose模式,我是否也應該提供析構函數?順便說一下,我只處理託管資源(例如Entity Framework DataContext)。

在這種情況下,沒有。原因是,當你的班級被GC抓住時,所有這些對象也將由GC處理。在這種情況下沒有理由添加析構函數的開銷。

這是IDisposable的複雜性的一部分 - 確實有應該比標準實施更多,這取決於使用情況。在這種情況下,您正在封裝一個實現IDisposable的資源。因此,允許用戶(間接)處理這些資源非常重要,但不需要處理析構函數,因爲沒有您直接「擁有」的非託管資源。如果你想了解更多的細節,我會在Part 3 of my series on IDisposable中介紹。


如果我提供了一個析構函數:如果我的類是從一個IDisposable接口,這可能已經提供了析構函數派生,那麼我應該提供一個嗎?我認爲在這種情況下從來沒有寫過析構函數,但是文檔說它會自動調用基類的析構函數。

在這種情況下,基類應該公開protected virtual void Dispose(bool disposing)形式的受保護方法。您會將資源清理邏輯放在那裏,因爲基類析構函數會爲您處理對此方法的調用。有關詳細信息,請參閱Part 2 of my series on IDisposable

+1

我認爲你的上面的評論,「MSDN文檔......只在你使用非託管資源時記錄正確的實現」是真正的問題。這種模式是安全的,因爲它經過了MS審查,但對於管理資源用例來說這並不完全有意義。 –

2

如果您正在編寫一個類,您不能強制每個使用該類的人都遵循預期的IDisposable模式。這就是爲什麼你需要析構函數後備。

即使「每個人」都是「只有你」,你是人類,有時會犯錯誤。

+0

是的。但請記住,它不能保證即使運行。 –

+2

不是在寫「一堂課」時。只適用於非常罕見和非常特殊的課程。 –

+0

在這種情況下,這是不正確的。沒有理由添加終結器的開銷,因爲沒有這個類「擁有」的非託管資源。如果需要,實體框架的上下文類應該自行管理它。 –

17

我實際上不會回答你的兩個問題,但我會提供意見:

所陳述的理由是,它是一個「回退」,這就是所謂的情況下,程序員忘記調用Dispose()

如果是要求的方法傳,也就是說,一個非空字符串的來電者,那麼你是完全的權利之內拋出一個異常,如果他們傳遞null,對不對?來電者違反了合同;這是特殊的行爲,所以你拋出一個異常。你不會想,哦,來電者「忘記」通過一個有效的論證,我想我會接受不好的投入和士兵。例如,這樣做有效地將方法的契約從「null是不可接受的並且將產生異常」改變爲「null是可接受的並且被視爲空字符串」。

如果這是要求,用戶在完成時調用Dispose,但他們沒有,那麼這與調用方在調用方法時未能履行合同沒有區別。來電未能滿足要求,所以崩潰其程序。如果遇到未處理的對象,析構函數會拋出一個信息異常。就像來電者很快得知傳遞糟糕的論點給方法帶來的傷害,他們也會知道,沒有處理你的對象也會受到傷害。

要麼顯式配置對象是必需否則不是。如果有必要,那麼確保用戶這樣做。否則是隱藏他們的錯誤的

+5

+1 - 雖然我完全同意這一點,但我也建議,這樣做可能是一個很好的選擇,可以有條件地構建,只包含DEBUG構建中的析構函數。沒有真正的理由包含額外的開銷如果它純粹是爲了幫助正確使用,那麼它就是發佈版本中的析構函數,因爲它會影響GC的性能。 –

+2

@ReedCopsey:非常好,謝謝。 –

+2

@Eric:「讓他們的節目崩潰」......一如既往的表現力! :-)也許我會加入消息「你違反了我的合同!」在例外。 –

1

在這個問題中添加一些東西非常困難,這個問題在這裏沒有被很好的答案所觸及。

我會試着給出一個替代MSDN上倡導的配置模式。我從來沒有真正喜歡的是Dispose(bool)方法,所以我覺得這個模式is better如果你一定需要一個析構函數

public class BetterDisposableClass : IDisposable { 

    public void Dispose() { 
    CleanUpManagedResources(); 
    CleanUpNativeResources(); 
    GC.SuppressFinalize(this); 
    } 

    protected virtual void CleanUpManagedResources() { 
    // ... 
    } 
    protected virtual void CleanUpNativeResources() { 
    // ... 
    } 

    ~BetterDisposableClass() { 
    CleanUpNativeResources(); 
    } 

} 

但是,既然你已經發現,你真的不需要一個,你的模式是much simpler

public class ManagedDisposable : IDisposable { 

    // ... 

    public virtual void Dispose() { 
    _otherDisposable.Dispose(); 
    } 

    IDisposable _otherDisposable; 

} 
+0

+1託管/非託管的顯式分離是整齊而合乎邏輯的。但是你的方法缺乏''TasteJusticeException''來執行代碼合約(參見Eric Lippert的上面的答案,看看它是如何完成的)。 –

+1

您鏈接的文章很有趣且有幫助。 –

+0

@PeterMarks:謝謝!我的方法缺乏這種例外,因爲我缺乏Lippert的才華!你可以添加它! –

-1
Or if you know that your object that you are trying to dispose of Implements IDisposable 
why not do something like this 

StreamWriter streamWrt = null 
try 
{ 
streamWrt = new StreamWrite(); 
... do some code here 
} 
catch (Exception ex) 
{ 
Console.WriteLine(ex.Message) 
} 
Finally 
{ 
    if (streamWrt != null) 
    { 
    ((IDisposable)streamWrt).Dispose(); 
    } 
}