深入理解C#(二)_顯式資源釋放需實作IDisposable
於(.NET)C#中每一個型別都代表一種資源
針對您的應用程式所建立的大部分物件,您可以依賴.net 垃圾收集行程來處理記憶體管理。
不過,當您建立包含非受控資源的物件時,您必須在完成使用這些資源時明確地釋放它們。
資源又可細分成
託管資源 跟 非託管資源
託管資源:
1.由CLR管理分配及釋放的資源(例:從CLR中 new出的物件實體)
2.不需要顯式資源釋放,但若引用型別本身含有非託管資源,則需要自行實踐釋放
非託管資源:
1.不受CLR管理的物件(例:檔案、資料庫連接、Windows 核心物件、COM元件、GDI+..etc)
2.需要顯式資源釋放的,也即需要你寫代碼釋放
Test Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace Tip46 { class Program { static void Main(string[] args) { } } public class SampleClass : IDisposable { //創建一個非託管資源 private IntPtr nativeResource = Marshal.AllocHGlobal(100); //創建一個託管資源 private AnotherResource managedResource = new AnotherResource(); //讓類型知道自己是否已經被釋放的Flag private bool disposed = false; /// <summary> /// 實現IDisposable中的Dispose方法 /// </summary> public void Dispose() { //必為true Dispose(true);//執行與釋放 (Free)、釋放 (Release) 或重設 Unmanaged 資源相關聯之應用程式定義的工作。 GC.SuppressFinalize(this);//要求CLR要為指定之物件呼叫完成項(防止呼叫多餘的垃圾收集)。 } public void Close() { Dispose(); } ~SampleClass() { //必須為false Dispose(false); } /// <summary> /// 非密封類修飾用protected virtual /// 密封類修飾用private /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (disposed) { return; } if (disposing) { // 清理託管資源 if (managedResource != null) { managedResource.Dispose(); managedResource = null; } } // 清理非託管資源 if (nativeResource != IntPtr.Zero) { Marshal.FreeHGlobal(nativeResource); nativeResource = IntPtr.Zero; } disposed = true; } public void SamplePublicMethod() { if (disposed) { throw new ObjectDisposedException("SampleClass", "SampleClass is disposed"); } //省略 } } class AnotherResource : IDisposable { public void Dispose() { } } } |
顯式資源釋放基本設計流程:
Step1.實作IDisposable介面的Dispose方法
執行 Dispose 方法主要是用來釋放非受控資源。
(PS:有實作IDisposable介面的可以用using來包括自動釋放)
Step2.於Destructor 解構式(解構子、解構函數)
間接調用Dispose、
當物件被終止時,系統會自動執行解構函式,將物件佔用的記憶體釋放掉,且不能被使用者自行調用。由於.NET本身的自動GC機制,一般物件的記憶體釋放不需要寫在解構函式中。
解構函式會隱函呼叫Base class的Finalize()方法,
若Base class仍有Base class時,則會遞迴呼叫其Finalize( )方法
-->防範其他程式設計師沒有Call 到Dispose或用using包起來
Step3.提供一Close 方法(非必要是效仿C++風格)
間接調用Dispose
結論:
Dispose()和解構函式最大的差別在於Dispose( )可以由使用者自行呼叫,或透過using陳述式自動執行,而解構函式只能由系統決定時機自動執行。
Ref:
記憶體回收
https://docs.microsoft.com/zh-tw/dotnet/standard/garbage-collection/
using 陳述式 (C# 參考)
https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/using-statement
清除 Unmanaged 資源
https://docs.microsoft.com/zh-tw/dotnet/standard/garbage-collection/unmanaged
IDisposable.Dispose 方法
https://docs.microsoft.com/zh-tw/dotnet/api/system.idisposable.dispose?view=netcore-3.1
GC.SuppressFinalize(Object) 方法
https://docs.microsoft.com/zh-tw/dotnet/api/system.gc.suppressfinalize?view=netcore-3.1
使用實作 IDisposable 的物件
https://docs.microsoft.com/zh-tw/dotnet/standard/garbage-collection/using-objects
實作 Dispose 方法
https://docs.microsoft.com/zh-tw/dotnet/standard/garbage-collection/implementing-dispose
[C#]Effective C# 條款十八:實現標準Dispose模式
https://dotblogs.com.tw/larrynung/2011/03/10/21774
留言
張貼留言