深刻淺出OOP(三): 多態和繼承(動態綁定/運行時多態)

在前面的文章中,咱們介紹了編譯期多態、params關鍵字、實例化、base關鍵字等。本節咱們來關注另一種多態:運行時多態, 運行時多態也叫遲綁定。express

 

運行時多態或遲綁定、動態綁定

在C#語音中,運行時多態也叫方法重寫(overriding),咱們能夠在子類中overriding基類的同簽名函數,使用「virtual & override」關鍵字便可。ide

 

C#的New、Override關鍵字

image

建立一個console 示例工程,命名爲InheritanceAndPolymorphism。在Program.cs基礎上,再添加2個類文件,分別命名爲ClassA.cs、ClassB.cs。拷貝以下代碼:函數

public class ClassA    {        public void AAA()        {            Console.WriteLine("ClassA AAA");        }        public void BBB()        {            Console.WriteLine("ClassA BBB");        }        public void CCC()        {            Console.WriteLine("ClassA CCC");        }    }

 

ClassB:this

public class ClassB    {        public void AAA()        {            Console.WriteLine("ClassB AAA");        }        public void BBB()        {            Console.WriteLine("ClassB BBB");        }        public void CCC()        {            Console.WriteLine("ClassB CCC");        }    }

在上面的代碼中,咱們能夠看到ClassA、ClassB有一樣簽名的方法,能夠在program.cs中直接使用。lua

咱們對代碼再作休整,結構以下:spa

/// <summary>
    /// ClassB, acting as a base class    /// </summary>
    public class ClassB    {        public void AAA()        {            Console.WriteLine("ClassB AAA");        }        public void BBB()        {            Console.WriteLine("ClassB BBB");        }        public void CCC()        {            Console.WriteLine("ClassB CCC");        }    }    /// <summary>
    /// Class A, acting as a derived class    /// </summary>
    public class ClassA : ClassB    {        public void AAA()        {            Console.WriteLine("ClassA AAA");        }        public void BBB()        {            Console.WriteLine("ClassA BBB");        }        public void CCC()        {            Console.WriteLine("ClassA CCC");        }    }

 

Program.cscode

 
     
        Main(= ==

F5,運行代碼,結果以下:對象

ClassA AAAblog

ClassA BBB繼承

ClassA CCC

ClassB AAA

ClassB BBB

ClassB CCC

ClassB AAA

ClassB BBB

ClassB CCC

但同時,在VS的Output窗口,咱們得到了3個Warnings:

'InheritanceAndPolymorphism.ClassA.AAA()' hides inherited member

'InheritanceAndPolymorphism.ClassB.AAA()'. Use the new keyword if hiding was intended.

'InheritanceAndPolymorphism.ClassA.BBB()' hides inherited member

'InheritanceAndPolymorphism.ClassB.BBB()'. Use the new keyword if hiding was intended.

'InheritanceAndPolymorphism.ClassA.CCC()' hides inherited member

'InheritanceAndPolymorphism.ClassB.CCC()'. Use the new keyword if hiding was intended.

這些Warnings的緣由是由於子類和基類的AAA、BBB、CCC方法簽名相同,儘管從執行上看優先執行子類同簽名的方法,可是可能會有潛在的問題,故Warnings提出。

 

重構實驗

基於上面的Warning,咱們手動修改代碼,看看如何消除這些Warnings。

先給子類添加new、override關鍵字試試:

/// <summary>
    /// Class A, acting as a derived class    /// </summary>
    public class ClassA : ClassB    {        public override void AAA()        {            Console.WriteLine("ClassA AAA");        }        public new void BBB()        {            Console.WriteLine("ClassA BBB");        }        public void CCC()        {            Console.WriteLine("ClassA CCC");        }    }

 

執行的結果是報錯了:

Error: 'InheritanceAndPolymorphism.ClassA.AAA()': cannot override inherited member 'InheritanceAndPolymorphism.ClassB.AAA()' because it is not marked virtual, abstract, or override

 

image

從這個錯誤提示信息看,咱們須要修改基類方法,如添加virtual關鍵字。

 
     
            
     
           
     
        Main(= = =

執行,則無Warning了,經過這個實例,咱們得知經過在基類添加Virtual關鍵字受權其子類可override基類同簽名方法的權限,方便了OOP的擴展。

 

3個類的運行時多態

ClassA\ClassB基礎上,下面添加ClassC,看看3個類繼承關係的運行時多態:

 
     
            
     
            
     
         
     
        Main(= = =

運行結果:

ClassB AAA

ClassB BBB

ClassA CCC

ClassB AAA

ClassB BBB

ClassA CCC

ClassC AAA

ClassA BBB

ClassA CCC

若是基類聲明瞭virtual 關鍵字,子類可以使用override修飾符實現運行時多態:只有在編譯器動態決定是否被調用。

若是未標明virtual或非virtual,則方法是否被調用在編譯期就能決定。

 

image

 

再看看下面的例子:

internal class A    {        public virtual void X()        {        }    }    internal class B : A    {        public new void X()        {        }    }    internal class C : B    {        public override void X()        {        }    }

F5運行,結果報錯了:

Error: 'InheritanceAndPolymorphism.C.X()': cannot override inherited member 'InheritanceAndPolymorphism.B.X()' because it is not marked virtual, abstract, or override

錯誤的緣由是A中定義了virtual的X函數,在B中用new關鍵字隱藏了A中的X函數。當C嘗試經過override關鍵字的時候,是得到不了A中的virtual關鍵字X函數的,既在C中X函數爲非Virtual的,故不能override。

 

切斷關係

           
     
        Main(= =

執行結果以下:

Class: A ; Method X Class: C ; Method X

image

在這裏,咱們經過在B類中添加new Virtual修飾符,而後在C中便可使用B中Virtual的X函數了。

 

4個類的運行時多態

image

在上面繼承上,在運行時多態中添加第四個類:ClassD。

 
     
        
     
        
     
         
     
        
     
        Main(= = ==

 

執行結果以下:

ClassB XXX

ClassB XXX

ClassD XXX

ClassD XXX

第一行輸出中,來自a.XXX()函數 , 咱們在 ClassA中定義了XXX函數,而後在ClassB中使用new關鍵字切斷了virtual關係--對子類而言。所以XXX函數從ClassC開始成爲新的virtual函數,在這個代碼中a是ClassD的實例,可是聲明的爲ClassA,故從下往上找,找到ClassB的XXX函數,打印並輸出結果。

 

永無止境的循環

 

 
     
        
     
        
     
        Main(=

運行報錯:

Error: {Cannot evaluate expression because the current thread is in a stack overflow state.}

在這個例子中,((ClassA)this).XXX(); 致使了循環調用,修改成base.XXX便可修復這個強轉致使的循環調用。

 

結論

  • 在C#中,子類對象可賦值給一個基類對象;相反須要強轉。

  • override關鍵字用於子類重寫同簽名的基類virtual函數

  • 用new和override可重寫基類virtual的同簽名函數

  • virtual修飾符的函數,只能在運行時決定是否被執行

  • 函數未用virtual修飾,則在編譯期便可決定是否被調用

 

原文連接:Diving in OOP (Day 3): Polymorphism and Inheritance (Dynamic Binding/Run Time Polymorphism)

相關文章
相關標籤/搜索