在C#中定義類時,常常不需要定義相關的建構函式和解構函式,因為基類System.Object提供了一個預設的實現方式。但是,如果需要,可以提供自己的建構函式和解構函式,以便初始化對象和清理對象。
使用下述文法把一個簡單的建構函式添加到類中:
class MyClass
{
public MyClass()
}
這個建構函式與包含它的類同名,且沒有參數(使之成為類的預設建構函式),這是一個公用函數,所以類的對象可以使用這個建構函式進行執行個體化。
也可以使用私人的預設建構函式,即這個類的對象執行個體不能用這個建構函式來建立:
class MyClass
{
private MyClass()
{
//Constructor code.
}
}
最後,也可以用相同的方式給類添加非預設的建構函式,其方法是提供參數,例如:
class MyClass
{
public MyClass()
{
//Default constructor code.
}
}
public MyClass(int myInt)
{
//Nondefault constructor code (uses myInt)
}
解構函式使用略微不同的文法來聲明。在.NET中使用的解構函式(由System.Object類提供)叫作Finalize(),但這不是我們用於聲明解構函式的名稱。使用下面的代碼,而不是重寫Finalize():
class MyClass
{
~MyClass()
{
//Destructor body.
}
}
類的解構函式有帶有~首碼的類名(與建構函式的相同)來聲明。當進行記憶體回收時,就執行解構函式中的代碼,釋放資源。在調用這個解構函式後,還將隱式地調用基類的解構函式,包括System.Object根類中的Finalize()調用。這個技術可以讓.NET Framework確保調用Finalize(),因為重寫Finalize()是指基類調用需要顯示地執行,這是有潛在危險的。
建構函式的執行序列
如果在類的建構函式中執行多個任務,把這些代碼放在一個地方是非常方便的。使用一個方法就可以把代碼放在一個地方,而C#提供了一個更好的方式。任何建構函式都可以進行配置,在執行自己的代碼前調用其他建構函式。
在討論建構函式前,先看看在預設情況下,建立類的執行個體時會發生什麼情況。在開發過程中,對象常常並沒有按照希望的那樣執行,而是在調用建構函式時出現錯誤。這常常是因為類繼承結構中的某個基類沒有正確執行個體化,或者沒有正確地給其類建構函式提供資訊。理解在對象生命週期的這個階段發生的事情,將更容易解決這類問題。
為了執行個體化派生的類,必須執行個體化它的基類。而要執行個體化這個基類,又必須執行個體化這個基類的基類,這樣一直到執行個體化System.Object為止。結果是無論使用什麼建構函式執行個體化一個類,總是要先調用System.Object.Object()。
如果對一個類使用非預設的建構函式,預設的情況是調用基類的預設建構函式(這裡與原書不一致。原書的內容是:"如果對一個類使用非預設的建構函式,預設的情況下是在其基類上使用匹配於這個建構函式簽章的建構函式。如果沒有找到這樣的建構函式,就使用基類的預設建構函式(根類System.Object總是要使用預設的建構函式,因為這個類沒有非預設的建構函式)。")。考慮下面的對象結構:
class
MyBaseClass
{
public MyBaseClass()
{
Console.WriteLine("Call MyBaseClass().");
}
public MyBaseClass(int i)
{
Console.WriteLine("Call MyBaseClass(int i)");
}
}
class
MyDerivedClass: MyBaseClass
{
public MyDerivedClass()
{
Console.WriteLine("Call MyDerivedClass().");
}
public MyDerivedClass(int i)
{
Console.WriteLine("Call MyDerivedClass(int i).");
}
public MyDerivedClass(int i, int j)
{
Console.WriteLine("Call MyDerivedClass(int i,int j)");
}
}
註:下面的內容與原書有差異,略有修改。My Code是在.NET 3.5環境下運行。
如果以下面的方式執行個體化MyDerivedClass:
MyDerivedClass myObj = new MyDerivedClass();
則事件的順序如下:
- 執行System.Object.Object()建構函式。
- 執行MyBaseClass.MyBaseClass()建構函式。
- 執行MyDerivedClass.MyDerivedClass()建構函式。
另外,如果使用下面的語句:
MyDerivedClass myObj = new MyDerivedClass(4);
則事件的順序如下:
- 執行System.Object.Object()建構函式。
- 執行MyBaseClass.MyBaseClass()建構函式。
- 執行MyDerivedClass.MyDerivedClass(int i)建構函式。
最後,如果使用下面的語句:
MyDerivedClass myobj = new MyDerivedClass(4,8);
則事件的順序如下:
- 執行System.Object.Object()建構函式。
- 執行MyBaseClass()建構函式。
- 執行MyDerivedClass.MyDerivedClass(int i,int j)建構函式。
這個系統會正常工作,並確保衍生類別中的建構函式可以訪問繼承的成員。但是,有時需要對發生的事件進行更多的控制。例如,在上面的執行個體化執行個體中,事件的順序如下:
- 執行System.Object.Object()建構函式。
- 執行MyDerivedClass(int i)建構函式。
- 執行MyDerivedClass.MyDerivedClass(int i,int j)建構函式。
使用這個順序把可以使用int i參數的代碼放在MyBaseClass(int i)中,即MyDerivedClass(int i,int j)建構函式要做的工作比較少,只需處理int j參數(假定int i參數在兩種情況下有相同的含義,雖然事情並非如此,但實際上我們常常做這樣的安排)。只有願意,C#就可以指定這種操作。
為此,只需在衍生類別的建構函式定義中指定所使用的基類的建構函式,如下所示:
class
MyDerivedClass: MyBaseClass
{
public MyDerivedClass()
{
Console.WriteLine("Call MyDerivedClass().");
}
public MyDerivedClass(int i)
{
Console.WriteLine("Call MyDerivedClass(int i).");
}
public MyDerivedClass(int i, int j):base(i)
{
Console.WriteLine("Call MyDerivedClass(int i,int j)");
}
}
其中,base關鍵字指定.NET執行個體化過程中使用基類中匹配指定簽名的建構函式。
也可以使用這個關鍵字指定基類建構函式的字面值,例如使用MyDerivedClass的建構函式調用MyBaseClass非預設的建構函式:
class
MyDerivedClass: MyBaseClass
{
public MyDerivedClass():base(5)
{
Console.WriteLine("Call MyDerivedClass().");
}
public MyDerivedClass(int i)
{
Console.WriteLine("Call MyDerivedClass(int i).");
}
public MyDerivedClass(int i, int j):base(i)
{
Console.WriteLine("Call MyDerivedClass(int i,int j)");
}
}
MyDerivedClass myDerivedObject = new
MyDerivedClass();
這段代碼將執行下述系列:
- 執行System.Object.Object()建構函式。
- 執行MyBaseClass.MyBaseClass(int i)建構函式。
- 執行MyDerivedClass.MyDerivedClass()函數。
除了base關鍵字外,這裡還可以使用另一個關鍵字this。這個關鍵字指定在調用指定的建構函式前,.NET執行個體化過程中對當前類使用非預設的建構函式。例如:
class
MyDerivedClass: MyBaseClass
{
public MyDerivedClass():this(5,6)
{
Console.WriteLine("Call MyDerivedClass().");
}
public MyDerivedClass(int i)
{
Console.WriteLine("Call MyDerivedClass(int i).");
}
public MyDerivedClass(int i, int j):base(i)
{
Console.WriteLine("Call MyDerivedClass(int i,int j)");
}
}
MyDerivedClass myDerivedObject = new
MyDerivedClass();
這段代碼將執行下述系列:
- 執行System.Object.Object()建構函式。
- 執行The MyBaseClass.MyBaseClass(int i)建構函式。
- 執行The MyDerivedClass.MyDerivedClass(int i, int j)建構函式。
- 執行The MyDerivedClass.MyDerivedClass()建構函式。
唯一的限制是使用this或base關鍵字只能指定一個建構函式。但是,如上一個樣本所示,這並不是一個嚴格的限制,因為我們仍可以構造相當複雜的執行序列。