C# 構(gòu)造函數(shù)的執(zhí)行序列
如果在類的構(gòu)造函數(shù)中執(zhí)行多個任務(wù),把這些代碼放在一個地方是非常方便的,這與把代碼放在函數(shù)中具有相同的優(yōu)勢。使用一個方法就可以把代碼放在一個地方,但C#提供了一種更好的方式。任何構(gòu)造函數(shù)都可以配置為在執(zhí)行自己的代碼前調(diào)用其他構(gòu)造函數(shù)。 .
在討論構(gòu)造函數(shù)前,先看一下在默認情況下,創(chuàng)建類的實例時會發(fā)生什么。除了前面說過的便于把初始化代碼集中起來外,還要了解這些代碼。在開發(fā)過程中,由于調(diào)用構(gòu)造函數(shù)時可能出現(xiàn)錯誤,對象常常并沒有按照預(yù)期的那樣執(zhí)行。發(fā)生構(gòu)造函數(shù)調(diào)用錯誤常常是因為類繼承結(jié)構(gòu)中的某個基類沒有正確實例化,或者沒有正確地給基類構(gòu)造函數(shù)提供信息。如果理解在對象生命周期的這個階段發(fā)生的事情,將更利于解決此類問題。
為了實例化派生的類,必須實例化它的基類。而要實例化這個基類,又必須實例化這個基類的基類,這樣一直到實例化Systera.ObjeCt(所有類的根)為止。結(jié)果是無論使用什么構(gòu)造函數(shù)實例化一個類,總是首先調(diào)用System.Object.Object()。
無論在派生類上使用什么構(gòu)造函數(shù)(默認的構(gòu)造函數(shù)或非默認的構(gòu)造函數(shù)),除非明確指定,否則就使用基類的默認構(gòu)造函數(shù)(稍后將介紹如何改變這個行為)。下面介紹一個簡短示例,來演示執(zhí)行順序??紤]下面的對象層次結(jié)構(gòu):
public class MyBaseClass
{
public MyBaseClass()
}
public MyBaseClass(int i)
{
}
}
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass()
{
}
public MyDerivedClass(int i)
{
}
public MyDerivedClass(int i, int j)
{
}
}
如果以下面的方式實例化MyDerivedClass:
MyDerivedClass myObj = new MyDerivedClass();
則執(zhí)行順序如下:
?執(zhí)行 System.Object.Object()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass()構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass()構(gòu)造函數(shù)。
另外,如果使用下面的語句:
MyDerivedClass myObj = new MyDerivedClass(4)?
則執(zhí)行順序如下:
?執(zhí)行 System.ObjectObject()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass()構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass(int i)構(gòu)造函數(shù)。
最后,如果使用下面的語句:
MyDerivedClass myObj = new MyDerivedClass(4, 8);
則執(zhí)行順序如下:
?執(zhí)行 System.Object.Object()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass()構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass(int i, int j)構(gòu)造函數(shù)。
大多數(shù)情況下,這個系統(tǒng)都能正常工作。但是,有時需要對發(fā)生的事件進行更多控制。例如,在上面的實例化示例中,可能想得到如下所示的執(zhí)行順序:
?執(zhí)行 System.Object.Object()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass(int i)構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass(int i,int j)構(gòu)造函數(shù)。
使用這個順序,可以把使用int i參數(shù)的代碼放在MyBaseClass(int i)中,即MyDerivedClass(int i,int j)購造函數(shù)要做的工作較少,只需要處理intj參數(shù)(假定int i參數(shù)在兩種情況下的含義相同,雖然事情并非總是如此,但實際上我們常做這樣的安排)。只要愿意,C#就可以指定這種操作。
為此,只需要使用構(gòu)造函數(shù)初始化器(constructor initializer),它把代碼放在方法定義的冒號后面。例如,可以在派生類的構(gòu)造函數(shù)定義中指定所使用的基類構(gòu)造函數(shù),如下所示:
public class MyDerivedClass : MyBaseClass
{
...
public MyDerivedClass(int i, int j) : base(i)
{
}
}
其中,base關(guān)鍵字指定.NET實例化過程使用基類中具有指定參數(shù)的構(gòu)造函數(shù)。這里使用了一個int參數(shù)(其值通過參數(shù)i傳遞給MyDerivedClass構(gòu)造函數(shù)),所以將使用MyBaseClass(int i)。這么做將不會調(diào)用MyBaseClass(),而是執(zhí)行本例前面列出的事件序列一也就是我們希望執(zhí)行的事件序列。
也可以使用這個關(guān)鍵字指定基類構(gòu)造函數(shù)的字面值,例如,使用MyDerivedClass的默認構(gòu)造函數(shù)來調(diào)用MyBaseClass的非默認構(gòu)造函數(shù):
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass() : base(5)
{
}
...
}
這段代碼將執(zhí)行下述序列:
?執(zhí)行 System.Object.Object()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass(int i)構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass()構(gòu)造函數(shù)。
除了 base關(guān)鍵字外,這里還可將另一個關(guān)鍵字this用作構(gòu)造函數(shù)初始化器。這個關(guān)鍵字指定在調(diào)用指定的構(gòu)造函數(shù)前,.NET實例化過程對當前類使用非默認的構(gòu)造函數(shù)。例如:
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass() : this(5, 6)
{
}
...
public MyDerivedClass(int i, int j) : base⑴
{
}
}
使用MyDerivedClass.MyDerivedClass()構(gòu)造函數(shù),將得到如下執(zhí)行順序:
?執(zhí)行 System.Object.Object()構(gòu)造函數(shù)。
?執(zhí)行 MyBaseClass.MyBaseClass(int i)構(gòu)造函數(shù)。
?執(zhí)行 MyDerivedClass.MyDerivedClass(int i,int j)構(gòu)造函數(shù)。
唯一的限制是使用構(gòu)造函數(shù)初始化器只能指定一個構(gòu)造函數(shù)。但如上例所示,這并不是一個很嚴格的限制,因為我們?nèi)钥梢詷?gòu)造相當復(fù)雜的執(zhí)行順序。
注意在定義構(gòu)造函數(shù)時,不要創(chuàng)建無限循環(huán)。例如:
public class MyBaseClass
{
public MyBaseClass() : this(5)
{
}
public MyBaseClass(int i) : this()
{
}
}
使用上述任何一個構(gòu)造函數(shù),都需要首先執(zhí)行另一個構(gòu)造函數(shù),而另一個構(gòu)造函數(shù)要求首先執(zhí)行原構(gòu)造函數(shù)。這段代碼可以編譯,但如果嘗試實例化MyBaseClass,就會得到一個SystemOverflowException異常。
點擊加載更多評論>>