首先,裡氏替換原則。 這是理解多態所必須掌握的內容。對於裡氏替換原則維基百科給出的定義如下: 為什麼子類可以替換父類的位置,而程序的功能不受影響呢? 當滿足繼承的時候,父類肯定存在非私有成員,子類肯定是得到了父類的這些非私有成員(假設,父類的的成員全部是私有的,那麼子類沒辦法從父類繼承任何成員,也就不存在繼承的概念了)。既然子類繼承了父類的這些非私有成員,那麼父類對象也就可以在子類對象中調用這些非私有成員。所以,子類對象可以替換父類對象的位置。 來看下面的一段代碼: 復制代碼 class Program { static void Main(string[] args) { Person p = new Person(); Person p1 = new Student(); Console.ReadKey(); } } class Person { //父類的私有成員 private int nAge; public Person() { Console.WriteLine("我是Person構造函數,我是一個人!"); } public void Say() { Console.WriteLine("我是一個人!"); } } class Student : Person { public Student() { Console.WriteLine("我是Student構造函數,我是一個學生!"); } public void SayStude() { Console.WriteLine("我是一個學生!"); } } class SeniorStudent : Student { public SeniorStudent() { Console.WriteLine("我是SeniorStudent構造函數,我是一個高中生!"); } public void SaySenior() { Console.WriteLine("我是一個高中生!"); } } 復制代碼 我們運行打印出的結果是: 根據前面的構造函數的知識很容易解釋這個結果。那麼我們在Main()函數中添加如下的代碼: 復制代碼 static void Main(string[] args) { Person p = new Person(); p.Say(); Person p1 = new Student(); p1.Say(); Console.ReadKey(); } 復制代碼 在訪問的過程中,可以發現p只可以訪問父類的say 而p1也只可以訪問父類的Say方法 其實在上面的代碼中,就滿足了裡氏替換原則。子類的Student對象,替換了父類Person對象的位置。 那麼它們在內存中發生了些什麼呢?如下圖: 由上可以知道,當一個父類的變量指向一個子類對象的時候只能通過這個父類變量調用父類成員,子類獨有的成員無法調用。 同理我們可以推理出,子類的變量是不可以指向一個父類的對像的 但是當父類變量指向一個子類變量的時候,可以不可以把父類的變量轉化成子類的對象呢?看下圖 關於引用類型的兩種轉換方式: 由上面的代碼我們已經知道了一種轉換,就是在變量錢直接加需要轉換的類型,如下代碼: Student s2 = (Student)p1; 那麼第二種轉換方式就是使用as關鍵字,如下代碼: //將指向子類對象的變量轉化成子類類型 Student s2 = (Student)p1; //使用as關鍵字,轉換失敗返回一個null值 Student s3 = p1 as Student; 使用as關鍵字和第一種強制轉換的區別就是,第一種如果轉換失敗會拋異常,第二種轉換失敗則返回一個null值。 思維導圖總結如下: 二,虛方法 使用virtual關鍵字修飾的方法,叫做虛方法(一般都是在父類中)。 看下面的一段代碼: 復制代碼 class Person { private int nAge; public Person() { Console.WriteLine("我是Person構造函數,我是一個人!"); } //這裡定義了一個虛方法 public virtual void Say() { Console.WriteLine("我是一個人!"); } } class Student : Person { //子類使用override關鍵字改寫了父類的虛方法 public override void Say() { Console.WriteLine("我是一個學生!"); } public Student() { Console.WriteLine("我是Student構造函數,我是一個學生!"); } public void SayStude() { Console.WriteLine("我是一個學生!"); } } 復制代碼 緊接著在main()函數中添加如下的代碼: 復制代碼 static void Main(string[] args) { Person p = new Person(); p.Say(); Person p1 = new Student(); p1.Say(); Student s = new Student(); s.Say(); Console.ReadKey(); } 復制代碼 打印結果如下: 我們很明顯的可以發現,第二個表達式滿足裡氏替換原則,p1.Say()執行的應該是父類的Say()方法,但是這裡卻執行了子類的Say()方法。 這就是子類使用override關鍵字的Say()方法覆蓋了父類的用Virtual關鍵字修飾的Say()方法。 我們使用動態圖片看一下調試過程, ①首先是沒有使用任何關鍵字: 由上可以看出直接跳入父類,執行了父類的Say()方法; ②再看使用virtual和override關鍵字的動態調試圖片,如下: 可以看到直接到子類去執行override關鍵字修飾的Say()方法。 那麼如果父類使用virtual關鍵字修飾,而子類沒有重寫該方法時會怎麼樣呢?如下面的代碼: 復制代碼 class Program { static void Main(string[] args) { Person p1 = new Student(); p1.Say(); Console.ReadKey(); } } class Person { private int nAge; public Person() { Console.WriteLine("我是Person構造函數,我是一個人!"); } //這裡定義了一個虛方法 public virtual void Say() { Console.WriteLine("我是一個人!"); } } class Student : Person { //子類中沒有出現override關鍵字修飾的方法 public void SayStude() { Console.WriteLine("我是一個學生!"); } }