C# 資源回收

蛮哥哥發表於2024-05-26

在C#中,資源主要分為託管資源(Managed Resources)和非託管資源(Unmanaged Resources)。瞭解這兩種資源的區別對於正確實現IDisposable介面和確保資源得到合理管理是非常重要的。

託管資源(Managed Resources)

託管資源是由.NET執行時直接管理的資源。這些資源通常由.NET框架提供,例如:

  • 字串(String)
  • 陣列(Array)
  • 集合(Collection)
  • 其他.NET類庫物件

託管資源的生命週期由.NET垃圾回收器(Garbage Collector,GC)管理。GC會自動回收不再使用的物件,釋放它們佔用的記憶體。由於GC的存在,開發者通常不需要手動釋放託管資源。

非託管資源(Unmanaged Resources)

非託管資源不是由.NET執行時管理的資源。這些資源需要程式設計師顯式釋放,例如:

  • 檔案控制代碼(File Handles)
  • 視窗控制代碼(Window Handles)
  • 網路連線(Network Connections)
  • 資料庫連線(Database Connections)
  • 硬體資源(Hardware Resources)

有個很典型的特徵時,託管資源的資源釋放流程是很標準的;而非託管資源的釋放很可能只有具體的開發者才知道,比如一個網路連線在釋放前可能會進行一系列特定的握手和訊息通知操作,這寫操作GC沒法自動完成,必須開發者自己實現IDisposable介面完成,因此非託管資源的生命週期不由GC管理,如果不正確釋放,可能會導致資源洩漏或非預期事件發生。

實現IDisposable介面

當你的類使用了一些非託管資源時,應該實現IDisposable介面,以確保這些資源能夠被正確釋放。IDisposable介面要求實現一個Dispose方法,該方法用於釋放資源。通常,Dispose方法會有兩個版本:

  • 一個公共的Dispose方法,用於釋放託管和非託管資源。
  • 一個受保護的Dispose(bool disposing)方法,用於區分託管資源和非託管資源的釋放。

Dispose(bool disposing)方法中:

  • disposingtrue時,釋放託管資源。
  • disposingfalse時(通常在解構函式中呼叫),只釋放非託管資源。

示例

public class ResourceHolder : IDisposable
{
    // 用於標記資源是否已經被釋放
    private bool _disposed = false;

    // 假設這是一個非託管資源
    private IntPtr _unmanagedResource;

    public void UseResource()
    {
        if (!_disposed)
        {
            // 使用資源
        }
        else
        {
            throw new ObjectDisposedException("ResourceHolder");
        }
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 釋放託管資源
            }

            // 釋放非託管資源
            if (_unmanagedResource != IntPtr.Zero)
            {
                // 假設的釋放非託管資源的程式碼
                // NativeMethods.ReleaseResource(_unmanagedResource);
                _unmanagedResource = IntPtr.Zero;
            }

            _disposed = true;
        }
    }

    ~ResourceHolder()
    {
        // 確保資源被釋放
        Dispose(false);
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        using (ResourceHolder resource = new ResourceHolder())
        {
            resource.UseResource();
            // 其他程式碼...
        }

        // 此時ResourceHolder的Dispose方法會被自動呼叫
    }
}

ResourceHolder類管理了一個非託管資源(假設為一個指標)。Dispose方法確保了資源被正確釋放,而解構函式確保了即使沒有顯式呼叫Dispose方法,資源也不會洩漏。

_disposed欄位用於跟蹤資源是否已經被釋放,以防止多次釋放資源。

GC.SuppressFinalize(this)呼叫告訴垃圾回收器不需要為當前物件呼叫解構函式,因為資源已經被顯式釋放。

using語句確保了Dispose方法在MyResource物件不再需要時被呼叫,這是管理資源釋放的一種簡潔且安全的方式。

相關文章