文章出處

《解析C#類中的構造函數》

一.  C#中的構造函數概述:

C#中類包含數據成員和函數成員。函數成員提供了操作類中數據的某些功能,包括方法、屬性、構造器和終結器、運算符和索引器。

  1. 構造函數設計的定義

   構造器是允許將類型的實例初始化為良好狀態的一種特殊方法。

  1. 2.    構造函數設計的作用:

c#中創建一個引用類型的實例時,首先為實例的數據字段分配內存,然后初始化對象的附加字段(類型對象指針和同步塊索引),最后調用類型的實例構造器來設置對象的初始化狀態。

  1. 3.    對類沒有定義任何共有的或受保護的構造器,導致該類不能使用new在外部代碼中實例化,此方式的作用:

     (1).類僅用作某些靜態成員或屬性的容器,因此永遠不會實例化它。

(2).希望類僅通過調用某個靜態成員函數來實例化(對象實例化工廠方法)

     4.基類構造函數的使用:

  編譯器在調用基類的構造器前,會初始化任何使用了簡化語法的字段,以維持源代碼給人留下的“這些字段總是有一個值”的印象,如果基類的構造器調用了一個虛方法,它回調派生類中定義的一個方法,就可能出現問題。在這種情況下,用簡化語法來初始化的字段在虛方法調用之前就已經初始化了。

二.  C#類中構造函數的分類:

  1. 1.    實例構造函數:

(1).實例構造函數概述:

聲明一個與包含的類同名的方法,該方法沒有返回類型。【如果沒有提供任何構造函數,編譯器會在后臺創建一個默認的構造函數】

(2).實例構造函數的作用:

         A.把所有的成員字段初始化為標準的默認值。

B. 構造引用類型的對象時,在調用類型的實例構造器之前,為對象分配的內存總是先被歸零,構造器沒有顯示重寫的所有字段保證都有一個0或null值。實例構造器永遠不能被繼承。

C. 為了使代碼“可驗證”,類的實例構造器在訪問從基類集成的任何字段之前,必須先調用基類的構造器。如果派生類的構造器沒有顯示調用一個基類構造器,C#編譯器會自動生成對默認的基類構造器的調用。

  1. 2.    靜態構造函數:

(1).靜態構造函數概述:

靜態構造函數(類型構造器、類型初始化構造器):靜態構造函數(類型構造器)不能含有參數,沒有修飾符,只能編寫無參數的構造函數,這種構造函數只能執行一次,一個類也只能有一個靜態構造函數,當前面的構造函數是實例構造函數,只要創建類的對象,就會執行它。

 (2).靜態構造函數運行原理:

.NET運行庫沒有確保什么時候執行靜態構造函數,所以不應該把要求在某個特定時刻執行的代碼放在靜態構造函數中。也不能預計不同類的靜態構造函數按照什么順序執行。

        (3).靜態構造函數的調用:

    靜態構造函數沒有訪問修飾符,其他C#代碼從來不調用它,但是在類加載時,總是又.NET運行庫調用它。靜態構造函數只能訪問類的靜態成員,不能訪問類的實例成員。

 3.構造函數使用范圍:

     (1).無參數的實例構造函數與靜態構造函數可以在同一類中同時定義,有參的實例構造函數與靜態構造函數也可以在同一類中同時定義。【在類加載時執行靜態構造函數,而在創建實例時執行實例構造函數】

    (2). C#編譯器不允許值類型(結構)定義無參數的構造器,所以編譯器永遠不會生成自動調用它的代碼,沒有無參數構造器,值類型(結構)的字段總是被初始化為0或null。【C#不允許值類型(結構)定義無參數的構造器,但是CLR允許。C#運行值類型(結構)定義有參數的構造器】

三.  C#類中替代構造函數的方式:

1.在極少數情況下,可以在不調用實例構造器的前提下創建一個類型的實例。使用Object的MemberwiseClone方法,該方法的作用是分配內存,初始化對象的附加字段(類型對象指針和同步塊索引),然后將源對象的字節數據復制到新對象中。

2.在用運行時序列化器反序列化對象時,通常也不需要調用構造器,反序列化使用FormatterService類型的GetUninitialzedObject或者GetSafeUninitializedObject方法為對象分配內存,期間不會調用一個構造器。

四.  C#類中構造器的使用情形:

  1.靜態構造函數的使用:

     類中有一些靜態字段或屬性,需要在第一次使用類之前,從外部源中初始化這些靜態字段和屬性。

2.構造函數的調用:

在C#類中,構造器可以調用其他構造器。C#構造函數初始化器可以包含對同一類的另一個構造函數的調用,也可以包含對直接基類的構造函數的調用。初始化器中不能有多個調用。【使用this關鍵字實現初始化器,調用參數最匹配的那個構造器】構造函數初始化器在構造函數的函數體之前進行。

五.  C#類中類型構造器的性能:

         1.     類型構造器的調用:

JIT編譯器必須決定是否生成代碼來調用它,而CLR必須確保對它的調用是線程安全的。

  1. 2.     JIT編譯器在生成一個對類型構造器的調用,這個調用添加的位置情況:

(1).JIT編譯器可以剛好在創建類型的第一個實例之前,或者剛好在訪問類的一個非繼承的字段或成員之前生成這個調用。即為精確語義。

(2).JIT編譯器可以在首次訪問一個靜態字段或者一個靜態/實例方法之前,或者在調用一個實例構造器之前,隨便找一個時間生成調用。即為字段初始化前語義。

     3. .JIT對調用的位置選擇:

  “字段初始化前”語義是首選的,因為它使CLR能夠自由選擇調用類型構造器的時機,而CLR會盡可能地利用這一點來生成運行得更快的代碼。

4.C#編譯器如果看到一個類(BeforeFieldInit)包含進行了內聯初始化的靜態字段,會在類的類型定義表中生成一個添加了BeforeFiledInit元數據標記的記錄項。C#編譯器如果看到一個類包含顯示的類型構造器,就不會添加BeforeFiledInit元數據標記。【靜態字段只要在訪問之前初始化就可以了,具體什么時間無所謂。而顯式類型構造器可能包含具有副作用的代碼,所以需要在精確拿捏運行的時間】 

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()