首頁  >  文章  >  後端開發  >  什麼是構造器?引用類型是什麼?

什麼是構造器?引用類型是什麼?

零下一度
零下一度原創
2017-06-23 15:14:452223瀏覽
引用類型
建構器是將類型的實例初始化為良好狀態的特殊方法,在建立引用類型的實例時,首先為實例的資料欄位分配內存,然後初始化物件的附加欄位(類型物件指標和同步區塊索引),最後呼叫類型的實例建構器來設定物件的初始化狀態。
建構參考類型物件時,在電泳類型的實例建構器之前,為物件分配的記憶體總是先被歸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();
}
}
類型建構器的程式碼只能存取類型的靜態字段,而他的非常規用途就是初始化這些字段。

以上是什麼是構造器?引用類型是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn