OOP的多型和繼承

daxuesheng發表於2021-09-09

在OOP中,繼承有如下的定義:

  • 繼承是一種OOP的機制,用於派生繼承預定義的類

  • 在這個繼承關係中,預定義的類是基類,新類是子類

  • 繼承常常用於實現程式碼重用

  • 繼承允許子類複用基類非private的的資料和方法

繼承的實現

建立一個Console工程,命名為InheritanceAndPolymorphism。新增ClassA、ClassB類,並複製下面的程式碼:

ClassA:

   class ClassA
     {
        
     }

ClassB:

    class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

 

Program.cs中,呼叫ClassA

class Program
    {
        static void Main(string[] args)
        {

            ClassA a = new ClassA();
            a.Display1();
        }
    }

如果執行,肯定會報錯的。

Error: 'InheritanceAndPolymorphism.ClassA' does not contain a definition for 'Display1' and no extension method 'Display1' accepting a first argument of type 'InheritanceAndPolymorphism.ClassA' could be found

因為我們在ClassA中未定義Display1的方法。 下面我們重寫,使ClassA繼承自ClassB。


ClassA:

  class ClassA:ClassB
    {
        
    }

ClassB:

class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

再次執行,結果如下:

ClassB Display1

ClassA已經可以訪問其基類的Display1函式了,這個簡單的例項說明了繼承可複用基類的妙處。

再來看另外一個場景,假設ClassA也有一個Display1函式,簽名和其基類一樣的:

class ClassA:ClassB
    {
        public void Display1()
        {
            System.Console.WriteLine("ClassA Display1");
        }
    }

ClassB:

class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

執行後結果如下:

ClassA Display1

看起來結果是對的,ClassA預設呼叫了自己的Display1函式,但是Visual Studio有一個警告:

Warning: 'InheritanceAndPolymorphism.ClassA.Display1()' hides inherited member 'InheritanceAndPolymorphism.ClassB.Display1()'. Use the new keyword if hiding was intended.

C#中對方法的呼叫首先是查詢ClassA自己中有無Display1函式,再查詢其基類有無Display1函式。在基類和子類出現同樣函式的情況現實專案中是存在的,可能是基類程式碼過於陳舊了,子類既想用同簽名的函式,又無法停止基類的同簽名函式,故會出現這樣的警告---儘管邏輯正確,但是這種設計還是有一些瑕疵的。

 

我們再試試在CalssA中透過base呼叫基類同名方法的情況:

ClassA:

  class ClassA:ClassB
    {
        public void Display1()
        {
            Console.WriteLine("ClassA Display1");
            base.Display1();
        }
    }

ClassB:

class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

 

執行結果如下:

ClassA Display1

ClassB Display1

這個實驗說明C#提供了base關鍵詞,用於在繼承中子類呼叫基類的函式或者變數(非private型別)。

 

同樣的,在ClassA.Display1中呼叫其基類的Display2也是可以的,程式碼如下所示:


/// 
   /// ClassB: acting as base class 
   /// 
   class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
        public void Display2()
        {
            Console.WriteLine("ClassB Display2");
        }
    }

    /// 
    /// ClassA: acting as derived class
    /// 
    class ClassA : ClassB
    {
        public void Display1()
        {
            Console.WriteLine("ClassA Display1");
            base.Display2();
        }
    }

    /// 
    /// Program: used to execute the method.
    /// Contains Main method.
    /// 
    class Program
    {
        static void Main(string[] args)
        {
            ClassA a = new ClassA();
            a.Display1();
            Console.ReadKey();
        }
    }

 

執行結果如下:

ClassA Display1

ClassB Display2

 

那麼可否透過基類呼叫其子類的函式呢?


/// 
   /// ClassB: acting as base class 
   /// 
   class ClassB
    {
        public int x = 100;
        public void Display1()
        {
            Console.WriteLine("ClassB Display1");
        }
    }

    /// 
    /// ClassA: acting as derived class
    /// 
    class ClassA : ClassB
    {
        public void Display2()
        {
            Console.WriteLine("ClassA Display2");
        }
    }

    /// 
    /// Program: used to execute the method.
    /// Contains Main method.
    /// 
    class Program
    {
        static void Main(string[] args)
        {
            ClassB b = new ClassB();
            b.Display2();
            Console.ReadKey();
        }
    }

 

執行報錯:

Error: 'InheritanceAndPolymorphism.ClassB' does not contain a definition for 'Display2' and no extension method 'Display2' accepting a first argument of type 'InheritanceAndPolymorphism.ClassB' could be found

原因是繼承無法實現逆向呼叫,既基類無法呼叫子類。

除了建構函式和解構函式,子類繼承了其基類的一些(包括private的成員變數和成員函式,只是無法訪問)。

在C#中,一個類預設繼承的是object型別,object是C#所有引用型別的基類;同時,繼承具有傳遞性,如ClassC繼承自ClassB,ClassB繼承自ClassA,則ClassC可完全複用ClassA的資料和函式---ClassC繼承了ClassA。

C#中所有的型別都可被繼承嗎?


public class ClassW : System.ValueType
   {
   }

   public class ClassX : System.Enum
   {
   }

   public class ClassY : System.Delegate
   {
   }

   public class ClassZ : System.Array
   {
   }

執行結果:

'InheritanceAndPolymorphism.ClassW' cannot derive from special class 'System.ValueType'

'InheritanceAndPolymorphism.ClassX' cannot derive from special class 'System.Enum'

'InheritanceAndPolymorphism.ClassY' cannot derive from special class 'System.Delegate'

'InheritanceAndPolymorphism.ClassZ' cannot derive from special class 'System.Array'

 

執行的結果讓人抓狂

圖片描述

在C#中,自定義類無法繼承自C#內建的一些類,如System.ValueType, System.Enum, System.Delegate, System.Array, etc。

 

下面這個例子我們再看看C++中的多類繼承是否可在C#中實現:

  public class ClassW
    {
    }

    public class ClassX
    {
    }

    public class ClassY : ClassW, ClassX
    {
    }

執行結果:

Compile time Error: Class 'InheritanceAndPolymorphism.ClassY' cannot have multiple base classes: 'InheritanceAndPolymorphism.ClassW' and 'ClassX'.

 

執行結論是:C#僅支援單類繼承,不支援C++的這種星型繼承關係。 要使用星型繼承關係,請用介面實現。

 

那麼可否實現迴圈依賴繼承呢?


   public class ClassW: ClassY
    {
    }

    public class ClassX: ClassW
    {
    }

    public class ClassY :  ClassX
    {
    }

 

程式碼邏輯很簡單,ClassW繼承自ClassY,ClassX繼承自ClassW, ClassY繼承自ClassX。

但是編譯後報錯了:

Error: Circular base class dependency involving 'InheritanceAndPolymorphism.ClassX' and 'InheritanceAndPolymorphism.ClassW'.

我們得出一個結論,C#中不許環形依賴繼承。

 

例項物件的是否可賦值


ClassB:
public class ClassB
    {
        public int b = 100;
    }

ClassA:

    public class ClassA
    {
        public int a = 100;
    }

 

Program.cs 程式碼如下


/// 
    /// Program: used to execute the method.
    /// Contains Main method.
    /// 
    public class Program
    {
        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classA = classB;
            classB = classA;
        }
    }

我們嘗試判斷ClassA、ClassB的物件是否可賦值。

編譯的結果是:報錯了

Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA' Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassA' to 'InheritanceAndPolymorphism.ClassB'


儘管ClassA和ClassB裡面的資料成員變數a資料一致,都為100,但是這裡用等號比較的是型別--引用地址,故無法進行賦值。

 我們再來試試繼承關係的:

public class ClassB
    {
        public int b = 100;
    }

    public class ClassA:ClassB
    {
        public int a = 100;
    }

    /// 
    /// Program: used to execute the method.
    /// Contains Main method.
    /// 
    public class Program
    {
        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classA = classB;
            classB = classA;
        }
    }

 

ClassA繼承自ClassB,我們希望可以直接賦值其例項物件。

 

執行結果如下:

Error: Cannot implicitly convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA'.

執行結論:C#中子類物件可直接賦值給基類物件,基類物件需要往下強轉。程式碼修改如下:


public class ClassB
    {
        public int b = 100;
    }

    public class ClassA:ClassB
    {
        public int a = 100;
    }

    /// 
    /// Program: used to execute the method.
    /// Contains Main method.
    /// 
    public class Program
    {
        private static void Main(string[] args)
        {
            ClassB classB = new ClassB();
            ClassA classA = new ClassA();
            classB=classA;
            classA = (ClassA)classB;
        }
    }

 

這樣編譯就透過了。

如果ClassA不繼承自ClassB,則這種強轉在C#中是會報錯的:

Cannot convert type 'InheritanceAndPolymorphism.ClassA' to 'InheritanceAndPolymorphism.ClassB'

Cannot convert type 'InheritanceAndPolymorphism.ClassB' to 'InheritanceAndPolymorphism.ClassA'

 

 

本節結論

  • 無法阻止子類覆蓋基類同簽名方法

  • 繼承關係是子類的同簽名方法先查詢,再查詢其基類的

  • base關鍵字被C#用於在子類中呼叫基類函式、變數

  • 繼承關係不可逆轉

  • 除了建構函式、解構函式,子類繼承了基類的一些

  • 自定義類預設繼承自Object型別,但是C#的這些型別不能被繼承:System.ValueType, System.Enum, System.Delegate, System.Array, etc.

  • C#不支援從多類繼承

  • C#不支援迴圈繼承

  • 子類物件可直接賦值給基類,反之需要強轉。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4692/viewspace-2808813/,如需轉載,請註明出處,否則將追究法律責任。

相關文章