Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
針對 unmanaged resources,本章節提供了一個標準的設計模式:IDisposable 介面與 Finalizer。
此模式能夠讓繼承的子類別也能夠實作自身的清除資源邏輯,當類別中有非託管資源時,可善加利用。
IDisposable 介面:
public interface IDisposable
{
void Dispose( );
}
Dipose( ) 方法應該包含以下四個任務:
1. 清除所有非託管資源。
2. 清除所有託管資源(包含事件連結)。
3. 用旗標記錄該物件是否已執行過 Dipose,若在已執行 Dispose 的物件執行任何方法應該擲出例外(ObjectDisposedException)。
4. 呼叫 GC.SuppressFinalize( this ),通知 GC 此物件已不需執行 Finalizer。
為了讓繼承體系中的所有類別都能夠確實清除資源;需要加入一些設計巧思。
1. 利用 protected virtual void dipose( bool isDisposing )讓各類別實作各自的清除資源邏輯。
範例程式:
public class MyResourceHog : IDisposable
{
// Flag for already disposed.
private bool _isDisposed;
// Implementation of IDisposable.
// Call the virtual dispose method.
// Suppress Finalization.
public void Dispose( )
{
dispose( true );
GC.SuppressFinalize( this );
}
public void M1( )
{
if ( _isDisposed )
throw new ObjectDisposedException( "MyResourceHog",
"Called M1 method on disposed object." );
// Do some work.
}
// Virtual dispose method.
protected virtual void dispose( bool isDisposing )
{
if ( _isDisposed )
return;
if ( isDisposing )
{
// Free managed resources.
}
// Free unmanaged resources.
// Set _isDisposed = true.
_isDisposed = true;
}
// Add Finalizer only if this class directly contains
// unmanaged resources.
~MyResourceHog( )
{
dispose( false );
}
}
public class DerivedResourceHog : MyResourceHog
{
// Have its own disposed flag.
private bool _isDisposed;
protected override void dispose( bool isDisposing )
{
// Don't dispose more than once.
if ( _isDisposed )
return;
if ( isDisposing )
{
// Free managed resourced here.
}
// Free unmanaged resourced here.
// Let the base class free its resources.
// Base class is responsible for calling
// GC.SuppressFinalize( )
base.dispose( isDisposing );
// Set Derived class disposed flag.
_isDisposed = true;
}
// Add Finalizer only if this class directly contains
// unmanaged resources.
~DerivedResourceHog( )
{
dispose( false );
}
}
這裡有幾個重點:
• _isDisposed 旗標為各類別各自擁有,確保該類別是否已 Disposed 的正確性。
• Finalizer 視為一種保護機制,未正常呼叫 Dispose 時;仍會由 GC 加入 Freachable Queue 非同步執行(增加系統資源消耗)。
• Finalizer 不一定需要加入,端看該類別有無 unmanaged resources 需要釋放。
2. Finalizer 只做資源釋放,不應加入其他無關的操作。
public class BadClass
{
// Store a reference to a global reference.
public static readonly List<BadClass> _finalizedList = new List<BadClass>( );
public BadClass( )
{
}
~BadClass( )
{
// Add this object to the list.
// This object is reachable, no longer garbage.
// It's back !
_finalizedList.Add( this );
}
}
在此例中,加入 _finalizedList 中的 BadClass 物件有可能已經不可用,原因在於該物件已經執行過 Finalizer(該物件某些資源已被釋放)。
1. 遇到 unmanaged resources 由統一的設計模式處理,增加程式碼的可維護性。
2. 仔細考慮是否加入 Finalizer;在大部分的情境下(沒有 unmanaged resources),並不需要實作 Finalizer。
參考資料:
Deep Dive Into C# - Garbage Collection And Disposal - Part One
Deep Dive Into C# - Garbage Collection And Disposal - Part Two