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