[C#, CSharp, C Sharp 教學 教程 教材 Tutorial] 建構函式(Constructor)與解構函式(Destructor)
YehYeh\'s Notepad yehyeh@gmail.com 

[C#, CSharp] 建構函式與解構函式

建構函式(Constructor)

  • 建構函式(Constructor):每次類別被實體化成物件時,系統會自動執行對應的建構函式,為物件進行初始化的動作
  • 也叫建構子
  • 建構函式的名稱和所屬的類別名稱相同,不能有回傳值,所以不需設定回傳型別
  • 格式:public 類別名稱(引數型別 引數, ... ) { ... }
  • 沒有引數的建構函式稱為預設建構函式
  • class 類別
    {
        public 類別()   //建構函式
        {
            XXXXX
        }
    }
    
  • 以建構函式對類別成員做初始化
    class 類別
    {
        public string 實體欄位 = "";
    
        public 類別()   //建構函式
        {
            實體欄位 = "A";
        }
    }
    
    class 類別A
    {
        類別 物件 = new 類別();
        System.Console.Write(物件.實體欄位);    //顯示:A
    }
    
  • 使用this代表建構函式:
    • class 類別
      {
          public 類別()
          {
              System.Console.Write( "A " );
          }
      
          //使用this()代表先執行沒有引數的建構函式
          public 類別(string 參數) : this()
          {
              System.Console.Write( "B" );
          }
      }
      
      class 類別A
      {
          類別 物件 = new 類別();         //顯示:A B
      }
      
  • 若類別無建構函式,類別實體化時會執行系統預設的公用建構函式,以系統的預設值初始化類別的欄位
    • 各基礎型別的系統預設值如下:
      實值型別 預設值 實值型別 預設值
      bool false byte0
      char'\0'decimal0.0M
      double0.0Dfloat0.0F
      int0long0L
      long0Lsbyte0
      short0uint0
      ulong0ushort0
      enum依識別項型別而定
      struct實值型別欄位會設為預設值,參考型別設為null
  • 建構函式可以多載(Overloading)
    • class 類別
      {
          public 類別() { XXXXX }
          public 類別(int a) { XXXXX }
          public 類別(string a) { XXXXX }
          public 類別(int a, string b) { XXXXX }
      }
      
Δ 回到最上方

靜態建構函式(Static Constructor)

  • 靜態建構函式只有第一次實體化類別或第一次參考類別的靜態成員時會被執行一次,之後便不會再被執行
  • 格式:static 類別名稱( ) { ... }
  • 用來初始化靜態資料,或只需執行一次的動作
  • 每次實體化類別時,一般的建構函式會被執行一次
  • 程式設計師無法控制靜態建構函式被執行的時間
  • 靜態建構函式沒有存取修飾詞(public、private、...)及引數
  • class 類別
    {
        static 類別() { XXXXX }
    }
    
Δ 回到最上方

私有建構函式(Private Constructor)

  • 私有建構函式通常用於靜態類別只有靜態成員的類別確保類別不能被實體化
  • 類別名稱( ) { ... } private 類別名稱( ) { ... }
    • class 類別
      {
          public static string 類別欄位 = "A";
          public static void 類別方法()
          {
              System.Console.Write(類別欄位);
          }
      
          private 類別()  { }         //私有建構函式
      }
      
      class 類別A
      {
          public void 實體方法()
          {
              類別.類別方法();        //合法寫法
      
              類別 物件 = new 類別(); //錯誤寫法
          }
      }
      
  • 若類別只有私有建構函式,則類別不能被實體化
    • class 類別
      {
          private 類別()
          {
              XXXXX
          }
      }
      
  • 若建構函式沒有加存取修飾詞,預設為私有建構函式
    • class 類別
      {
          類別() { XXXXX }
      }
      
Δ 回到最上方

解構函式(Destructor)

  • 解構函式(Destructor):當物件被終止時,系統會自動執行解構函式,將物件佔用的記憶體釋放掉
    • 也叫解構子
    • .Net Framework會自動執行垃圾回收管理記憶體 一般物件的記憶體釋放不需要寫在解構函式中
    • 通常程式中有處理到Window Handle、印表機、檔案存取、網路連線、資料庫連線、...等Unmanaged的資源才需要設計解構函式,並在解構函式中釋放掉這些資源
    • 建構函式的名稱和所屬的類別名稱相同且前面要加上 ~
      • class 類別
        {
            ~類別()     //解構函式
            {
                XXXXX
            }
        }
        
    • 只有類別中可以定義解構函式 結構中不可以定義解構函式
    • 一個類別只能定義一個解構函式
    • 解構函式不能被繼承或多載
    • 解構函式不能有存取修飾詞及引數
    • 解構函式只能由系統自動執行,不可被使用者自行呼叫
    • 解構函式會隱函呼叫父類別的Finalize()方法 ,亦即系統會自動將解構函式轉為以下的型式
      • class 類別
        {
            protected override void Finalize()
            {
                try
                {
                    XXX
                }
                finally
                {
                    base.Finalize();
                }
            }
        }
        
    • 若父類別仍有父類別時,會遞迴呼叫其Finalize( )方法 解構函式 = Finalize( )
    • 每次執行解構函式時,系統會自動執行垃圾回收(Garbage Collection, GC) 空的解構函式只會浪費效能
      • 垃圾:類別實體化成物件後,程式即可對物件進行操作,若物件不再被使用,或形成環狀參考,則視為垃圾
Δ 回到最上方

繼承時的建構與解構

  • 子類別的建構函式若有呼叫父類別的建構函式,則會先執行父類別的建構函式,再執行子類別的建構函式
  • 子類別被系統回收時,會先執行子類別的解構函式,再執行父類別的解構函式
Δ 回到最上方

Dispose方法

  • Dispose方法
    • 在System.IDisposable介面中定義了Dispose()方法
    • 類別可以實作Dispose方法,並在其中撰寫釋放Unmanaged資源的程式碼
      • class 類別 : IDisposable
        {
            public void Dispose()
            {
                XXXX        //釋放Unmanaged資源
            }
        }
        
    • Dispose()和解構函式最大的差別在於Dispose( )可以由使用者自行呼叫,或透過using陳述式自動執行,而解構函式只能由系統決定時機自動執行
      • class 類別 : IDisposable
        {
            private void Dispose()
            {
                XXXX        //釋放Unmanaged資源
            }
        
            public void Close()
            {
                this.Dispose();
            }
        }
        
        class 類別A 
        {
            public void 方法1()     //使用者自行呼叫Dispose( )
            {
                類別 物件 = new 類別();
                物件.Close();       //程式執行到此時會呼叫Dispose( )
            }
        
            public void 方法2()     //使用using陳述式自動執行Dispose( )
            {
                using(類別 物件 = new 類別())
                {
                    XXXX
                }                   //程式執行到此時會自行呼叫Dispose( )
            }
        }
        
    • 一般會在Dispose方法中釋放Unmanaged的資源,若失敗則在垃圾回收機制自動執行解構函式時再釋放一次
    • 在Dispose中若釋放資源成功可以執行GC.SuppressFinalize(this),避免執行解構函式時重複釋放資源
      • class 類別 : IDisposable
        {
            private bool disposed = false;
        
            public void Dispose()
            {
                Dispose(true);
            }
        
            public void Dispose(bool disposing)
            {
                if(!this.disposed)      //如果Dispose沒有成功執行過
                {
                    try
                    {
                        if(disposing)   //如果是由使用者呼叫,則可以釋放Managed及Unmanaged資源
                        {
                            XXXX        //釋放Managed
                        }
        
                        XXXX            //釋放Unmanaged資源
        
                        disposed = true;
                        GC.SuppressFinalize(this);
                    }
                    catch(Exception ex)
                    {
                        XXX
                    }
                    finally
                    {
                        XXXX
                    }
        
                }
            }
        
            public void Close()
            {
                this.Dispose(true);
            }
        
            ~類別()
            {
                this.Dispose(false);
            }
        }
        
Δ 回到最上方