我們分別為父類和子類添加顯式的構造函數,代碼如下:
class Person
{
private int nAge;
protected string strName;
double douHeight;
public string strEateType;
//父類的構造函數
public Person()
{
Console.WriteLine("我是父類的構造函數");
}
public void Hello()
{
Console.WriteLine("我可以說Hello!");
}
public void Run()
{
Console.WriteLine("我可以跑!");
}
}
class Student : Person
{
private string strClass;
private string strAddress;
//子類的構造函數
public Student ()
{
Console.WriteLine("我是子類的構造函數");
}
}
我們使用VS的單步調試,來看父類和子類顯式構造函數的執行順序,如下圖(動態圖片,可以看到過程):
很容易的可以發現,當創建子類對象的時候
①先調用了子類的構造函數
②調用了父類的構造函數
③執行了父類的構造函數
④執行了子類的構造函數
那么為什么會這樣呢?
我嘗試通過反編譯看源碼來解釋這個原因,但是反編譯的結果如下,
沒有發現有什么特別的地方可以解釋這個原因。
最后還是查閱微軟的MSDN官方文檔找到了答案(原文地址點擊這里)
根據微軟官方的代碼示例,那么下面的代碼的效果也是相同的
//子類的構造函數 02 public Student () 03 { 04 Console.WriteLine("我是子類的構造函數"); 05 } 06 //這里的代碼和上面的代碼效果是相同的 07 public Student() 08 :base() 09 { 10 Console.WriteLine("我是子類的構造函數"); 11 }
也就是說只要在子類顯式的聲明了無參的構造函數,在實例化子類的對象是,子類的無參構造函數都會去調用父類無參的構造函數。
那么,如果父類沒有這個無參的構造函數則會報錯。
如下面的代碼:
class Person 02 { 03 private int nAge; 04 protected string strName; 05 double douHeight; 06 public string strEateType; 07 //父類的構造函數 08 //public Person() 09 //{ 10 // Console.WriteLine("我是父類的構造函數"); 11 //} 12 //父類的有參數的構造函數,這里覆蓋了無參的構造函數 13 public Person (string str) 14 { 15 Console.WriteLine("我是父類的構造函數{0}",str); 16 } 17 public void Hello() 18 { 19 Console.WriteLine("我可以說Hello!"); 20 } 21 public void Run() 22 { 23 Console.WriteLine("我可以跑!"); 24 } 25 } 26 class Student : Person 27 { 28 private string strClass; 29 private string strAddress; 30 //子類的無參構造函數 31 public Student () 32 { 33 Console.WriteLine("我是子類的構造函數"); 34 } 35 public Student(string strName) 36 { 37 Console.WriteLine("我的名字叫{0}",strName); 38 } 39 }
這時候編譯會報錯,
因為在父類中有參數的構造函數覆蓋了無參數的構造函數,所以在子類的無參數的構造函數沒辦法回調父類的無參數的構造函數初始化父類的成員變量。所以報錯。
那么在初始化子類的時候,為什么要調用父類的構造函數呢?
在初始化子類之前需要通過構造函數初始化父類的成員變量
父類的構造函數先於子類的構造函數執行的意義是什么呢?
當在父類的構造函數中和子類的構造函數中為父類的非私有成員變量賦不同默認值。當實例化子類,子類要調用構造函數初始化成員變量,如果先執行了子類的構造函數,再執行父類的構造函數,父類成員字段的值會覆蓋子類成員字段的值。但是我們想得到的是子類的屬性值。所以為了解決數據沖突,父類的構造函數要先於子類的構造函數執行。
如下面的代碼:
class Person 02 { 03 private int nAge; 04 private string strName; 05 double douHeight; 06 public string strEateType; 07 // 父類的構造函數 08 public Person() 09 { 10 //再父類中對strEateType賦初始值 11 this.strEateType = "吃飯"; 12 Console.WriteLine("我是父類的構造函數{0}", strEateType); 13 } 14 } 15 class Student : Person 16 { 17 private string strClass; 18 private string strAddress; 19 //子類的構造函數 20 public Student() 21 { 22 //在子類中對strEateType賦初始值 23 this.strEateType = "吃面條"; 24 Console.WriteLine("我是子類的構造函數{0}",strEateType); 25 } 26 }
這時候我們通過,聲明子類對象訪問strEateType的值,如下:
Student stu1 = new Student(); 2 //stu1. 3 string str = stu1.strEateType.ToString(); 4 Console.WriteLine(str); 5 Console.ReadKey();
這里肯定是要打印出子類的屬性strEateType的值,如果先執行子類構造函數對strEateType賦值,然后父類的構造函數賦值覆蓋strEateType的初始值。那么打印出的將是父類成員字段的值。所以,父類的構造函數先於子類的構造函數執行。
打印結果如下: