Chapter 3 - Item 21 : Always Create Generic Classes That Support Disposable Type Parameters

Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得

在設計泛型類別時,需考慮輸入的類別是否有實作 IDisposable;若沒有適當處理外部資源,會提高記憶體洩漏的風險。此節提供幾種設計方式,避免上述情況發生。

1. 當初始化輸入類別職責在泛型類別時,使用 using 區塊自動釋放資源。

public interface IEngine
{
    void doWork( );
}

public class EngineDriveOne<T> where T : IEngine, new()
{
    // May leaks resource, because we couldn't confirm whether driver was 
    // implemented IDisposabe or not.
    public void getThingsDone( )
    {
        T driver = new T( );
        driver.doWork( );
    }

    public void getThingsDone2( )
    {
        T driver = new T( );

        using ( driver as IDisposable )
            driver.doWork( );
    }
}

using 區塊是 C# 的語法糖。當離開 using 區塊時,若轉型成功則執行 Dispose;失敗則不執行。

2. 當輸入類別為泛型類別的類別成員時,讓泛型類別實作 IDisposable。

public sealed class EngineDriverTwo<T> : IDisposable
    where T : IEngine, new()
{
    private Lazy<T> _driver = new Lazy<T>( ( ) => new T( ) );

    public void getThingsDone( ) =>
        _driver.Value.doWork( );

    public void Dispose( )
    {
        if ( _driver.IsValueCreated )
        {
            var resource = _driver.Value as IDisposable;
            resource?.Dispose( );
        }
    }
}
Note:類別加上了 sealed 修飾字,如此一來就不會有子類別記憶體洩漏的問題(無法被繼承)。

3. 利用 Dependecy Injection 原則,將物件初始化與釋放資源的職責交給外部。

public sealed class EngineDriverThree<T> where T : IEngine
{
    private T _driver;

    // Dependency Injection
    public EngineDriverThree( T driver )
    {
        _driver = driver;
    }

    public void getThingsDone( ) =>
        _driver.doWork( );
}
結論:
1. 設計泛型類別時需考慮輸入類別的資源釋放。

2. 資源釋放的處理方式不只一種,視情況選擇最適合的方式。