引用類型 構造器是將類型的實例初始化為良好狀態的特殊方法,創建引用類型的實例時,首先為實例的數據欄位分配記憶體,然後初始化對象的附加欄位(類型對象指針和同步塊索引),最後調用類型的實例構造器來設置對象的初始化狀態。 構造引用類型對象時,在電泳類型的實例構造器之前,為對象分配的記憶體總是先被歸0,沒有被構 ...
引用類型 構造器是將類型的實例初始化為良好狀態的特殊方法,創建引用類型的實例時,首先為實例的數據欄位分配記憶體,然後初始化對象的附加欄位(類型對象指針和同步塊索引),最後調用類型的實例構造器來設置對象的初始化狀態。 構造引用類型對象時,在電泳類型的實例構造器之前,為對象分配的記憶體總是先被歸0,沒有被構造器顯示重寫的所有欄位都保證獲得0或null值。 和其他方法不同,實力構造器永遠不能被繼承,也就是說,類只有類自己定義的還順利構造器。由於永遠不能繼承實例構造器,所以實例構造器不能使用以下修飾符:Virtual,new,override,sealed和abstract。如果類沒有顯示定義任何構造器,C#編譯器將預設一個預設無參構造器,在她的實現中,只是簡單的調用了基類的無參構造函數。 如果類的修飾符為abstract,那麼編譯器生成的預設構造器的可訪問性就為product;否則,構造器會被賦予public可訪問屬性。如果基類沒有提供無參構造器,那麼派生類必須顯示調用一個基類構造器,否則編譯器會報錯。如果類的修飾符為static(sealed和abstract),編譯器根本不會再類的定義中生成預設的構造器。 一個類型可以定義多個實例構造器。每個構造器都必須有不同的簽名,而且每個都可以有不同的可訪問屬性。為了使代碼“可驗證”,類的實力構造器在訪問從基類繼承的任何欄位前,必須先調用基類的構造器。如果派生類的構造器沒有顯示調用一個基類構造器,C#編譯器會自動生成對預設的基類構造器的調用。最終,System.Object的公共無參構造器會得到調用。該構造器什麼都不做,會直接返回,由於System.Object沒有實例數據欄位,所以它的構造器無事可做。 極少數情況下可以在不調用實例構造器的前提下創建類型實例。一個典型的例子就是Object的MemberwiseClone方法。該方法的作用是分配記憶體,初始化對象的附加欄位,然後將源對象的自己數據複製到新對象中。另外,用運行時序列化器(runtime seriallizer)反序列化對象時,同程也不需要調用構造器。反序列化使用System.Runtime.Serialization.FormatterServices類型的GetUninitalizedObject或者GetSafeUninitailizedObject方法為對象分配記憶體,期間不會調用一個構造器。 提示: 不要再構造器中調用虛方法。原因是假如被實例化的類型重寫了虛方法,就會執行派生類型對虛方法的實現,但是在這個時候,尚未完成對繼承層次結構中所有欄位的初始化(被實例化的類型的構造器還沒有運行)。所以,調用虛方法會導致無法預測的行為。歸根到底,這是由於調用虛方法時,直到運行時之前都不會選擇執行該方法的實際類型。 值類型(struct)構造器 值類型(struct)構造器的工作方式與引用類型(class)的構造器截然不同。CLR總是允許創建值類型的實例,並且沒有辦法阻止值類型的實例化。所以,值類型其實並不需要定義構造器,C#編譯器根本不會為值類型內聯預設無參構造器。來看下麵代碼: internal struct Point { public int m_x, m_y; } internal sealed class Reactangel { public Point m_TopLeft, m_bottomRight; } 為了構造一個Rectangle,必須使用new操作符,而且必須指定構造器。在這個例子中,調用的是C#編譯器自動生成的預設構造器。為Reatangle分配記憶體,記憶體中包含Point值類型的兩個實例。考慮到性能,CLR不會為包含在引用類型中的每個值類型欄位都主動調用構造器,但是,如前所述,值類型的欄位都會被初始化為0或null。 CLR確實允許為值類型定義構造器,但是必須顯示調用才會執行。 internal struct Point { public int m_x, m_y; public Point(int x, int y) { m_x = x; m_y = y; } } internal sealed class Reactangel { public Point m_TopLeft, m_bottomRight; public Reactangel() { this.m_TopLeft = new Point(1,2); this.m_bottomRight = new Point(100,200); } } 值類型的實例構造器只有顯示調用才會執行。因此,如果Rectangle的構造器沒有使用new操作符來調用Point的構造器,從而初始化Reatangle的m_TopLeft和m_bottomRight欄位,那麼兩個point欄位中的m_x和m_y欄位都將為0. 將上面代碼改寫: internal struct Point { public int m_x, m_y; public Point() { m_x = 5; m_y = 6; } } internal sealed class Reactangel { public Point m_TopLeft, m_bottomRight; public Reactangel() { } } 現在,構造新的Rectangle類時,兩個Point欄位中的m_x和m_y欄位會被初始化多少,是0還是5? 可能你會覺得C#編譯器會子啊Reactangel的構造器中生成代碼,為Reactangel的兩個欄位自動調用Point的預設無參構造器。但是,為了增強應用程式的運行時性能,C#編譯器不會自動生成這樣的代碼。實際上,即便值類型提供了無參構造器,許多編譯器也永遠不會生成代碼來調用它,為了執行值類型無參構造器,開發人員必須增加顯示調用值類型構造器的代碼。但是會由於這個原因Point‘的兩個欄位被初始化為0嗎?結果是: C#編譯器故意不允許值類型定義無參構造器,目的是防止開發人員對這種構造器在什麼時候調用產生迷惑。由於不能定義無參構造器,所以編譯器永遠不會生成自動調用它的代碼,沒有無參構造器,值類型的欄位總是被初始化為0或null。 類型構造器: 也稱為靜態構造器,類構造器或者類型初初始化器。類型構造器可應用與引用類型和值類型。實例構造器的作用是設置類型的實例的初始狀態。對應的,類型構造器的作用是設置類型的初始狀態。類型預設沒有定義類型構造器,如果定義,也只能有一個。此外,類型構造器永遠沒有參數。 internal sealed class SomeRefType { static SomeRefType() { //首次訪問時,執行這裡的代碼 } } internal struct SomeValType { static SomeValType() { //首次訪問時,執行這裡的代碼 } } 可以看出,定義類型構造器類似於定義無參實例構造器,區別在於必須標記為static。此外,類型構造器總是私有的。之所以私有,是為了防止任何開發人員寫代碼調用它,對他的調用總是有CLR負責。 提示: 雖然能在值類型中定義類型構造器,但永遠不要真的那麼做,因為CLR有時不會調用值類型的靜態構造器:例如 internal struct SomeValType { static SomeValType() { Console.WriteLine("這句話永遠不會顯示"); } public int m_x; } class Program { static void Main(string[] args) { SomeValType[] a = new SomeValType[10]; a[0].m_x = 123; Console.WriteLine(a[0].m_x); Console.ReadKey(); } } 類型構造器的代碼只能訪問類型的靜態欄位,並且他的非常規用途就是初始化這些欄位。