深入理解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

留言

這個網誌中的熱門文章

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header

經得起原始碼資安弱點掃描的程式設計習慣培養(三)_7.Cross Site Scripting(XSS)_Stored XSS_Reflected XSS All Clients

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題