1.創建一個對象
一個對象的創建過程主要分為內存分配和初始化兩個環節。在.NET中CLR管理的內存區域主要有三部分:棧、GC堆、LOH堆,棧主要用來分配值類型數據。它的管理是有系統控制的,而不是像GC堆那樣是由GC控制的。當線程執行完值類型實例所在方法後,這塊空間將會被自動釋放,一般棧的執行效率高不過容量有限。GC堆用來分配小對象實例,它是由GC完全控制內存的分配和回收。LOH堆則是為大對象實例准備的,它不會被壓縮且只在GC完全回收時才會回收。在IL中可以看到newobj、ldstr(創建string對象)、newarr(用於分配新的數組對象)、box(裝箱)等常見的創建對象的指令。當然在堆上也存在值類型,比如值類型作為類的字段時,它將存儲在堆中的實例對象空間,還有裝箱時也會讓堆上存在值類型。好了接下來我們來看看創建一個對象的內存分配,現在有一個Person類和Student類。那麼這句Student s = new Student() { studentId = 2, Id = 4 };執行完後s對象就被創建了,下面我畫了張圖來說明創建一個對象時內存的分配,其中s對象還有同步索引塊與類型對象指針我沒有畫出來。
public class Person { public int Id; public void Eat() { Console.WriteLine("Eat Pear"); } } public class Student:Person { public int studentId; public void GotoSchool() { Console.WriteLine("Go to School"); } }View Code
2.父類對象指向子類
我們在寫程序時為了實現多態一般都會使用父類對象指向子類。那麼當我寫入Person p=new Student();時便在堆中創建了一個子類對象,下面是關於父類對象指向子類的內存分配圖。我在Person中添加了虛方法和抽象方法,並在Student子類重寫了方法。從圖中可以看出一旦子類重寫了父類的虛方法或抽象方法,則Person方法表中的2個方法將會被子類覆蓋,我們可根據它來實現多態。另外在Student方法表中還有一個new void Eat()方法,不過它是無法被p調用的因為此時的new Eat()屬於子類。也就是說除了被覆蓋的方法外,p只能調用Person方法表中的方法,如果找不到則會繼續尋找Person父類的方法直到object。注意是不會往回找的,它不會去Student方法表中尋找方法。
public abstract class Person { public int Id; public void Eat() { Console.WriteLine( "在吃梨"); } public virtual void Walk() { Console.WriteLine("在散步"); } //抽象方法只能在抽象類中聲明,因此要在Person前加abstract,且只能聲明並必須在子類中實現。 public abstract void Run(); }View Code
public class Student:Person { public int studentId; public void GotoSchool() { Console.WriteLine("Go to School"); } public new void Eat() { Console.WriteLine("學生 吃蘋果"); } public override void Walk() { Console.WriteLine("學生 在散步"); } public override void Run() { Console.WriteLine("學生 在跑步"); } }View Code
3.指向孫類對象
現在我再添加一個Student的子類James,從上一個例子中已經知道只有override關鍵字重寫的方法父類才會調用,因此我將普通方法全部刪除。執行代碼為Person p = new James() { name = "James", studentId = 2, Id = 4 };代碼和內存分配圖如下,為了突出重點,圖中我就沒有畫字段了。從結果可以看到SayHi方法最後是被孫類的SayHi覆蓋了,從這裡可以看出繼承的傳遞性!
public abstract class Person { public int Id; public virtual void Eat() { Console.WriteLine( "在吃梨"); } public virtual void Walk() { Console.WriteLine("在散步"); } //抽象方法只能在抽象類中聲明,因此要在Person前加abstract,且只能聲明並必須在子類中實現。 public abstract void Run(); public virtual void SayHi() { Console.WriteLine("人說:你好!"); } }View Code
public class Student:Person { public int studentId; public virtual void Eat() { Console.WriteLine("學生 在吃梨"); } public override void Walk() { Console.WriteLine("學生 在散步"); } public override void Run() { Console.WriteLine("學生 在跑步"); } }View Code
public class James:Student { public string name; public override void Eat() { Console.WriteLine("James 在吃梨"); } public override void Walk() { Console.WriteLine("James 在散步"); } public override void Run() { Console.WriteLine("James 在跑步"); } public override void SayHi() { Console.WriteLine("James說:你好!"); } }View Code