深入理解OOP(二):多態和繼承(繼承)

来源:http://www.cnblogs.com/xijianyao/archive/2016/01/06/5106794.html
-Advertisement-
Play Games

本文是深入淺出OOP第二篇,主要說說繼承的話題。深入理解OOP(一):多態和繼承(初期綁定和編譯時多態)深入理解OOP(二):多態和繼承(繼承)深入理解OOP(三):多態和繼承(動態綁定和運行時多態)深入理解OOP(四):多態和繼承(C#中的抽象類)深入理解OOP(五):C#中的訪問修飾符(Publ...


本文是深入淺出OOP第二篇,主要說說繼承的話題。

 

繼承的介紹

在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也是可以的,代碼如下所示:

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

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

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

執行結果如下:

ClassA Display1

ClassB Display2

 

那麼可否通過基類調用其子類的函數呢?

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

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

    /// <summary>
    /// Program: used to execute the method.
    /// Contains Main method.
    /// </summary>
    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.ValueTypeSystem.EnumSystem.DelegateSystem.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 代碼如下

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;
    }

    /// <summary>
    /// Program: used to execute the method.
    /// Contains Main method.
    /// </summary>
    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;
    }

    /// <summary>
    /// Program: used to execute the method.
    /// Contains Main method.
    /// </summary>
    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.ValueTypeSystem.EnumSystem.DelegateSystem.Array, etc.
  • C#不支持從多類繼承
  • C#不支持迴圈繼承
  • 子類對象可直接賦值給基類,反之需要強轉

 

譯文地址:http://www.cnblogs.com/powertoolsteam/p/Diving-in-OOP-Polymorphism-and-Inheritance-Part.html

原文地址:Diving in OOP (Day 2): Polymorphism and Inheritance (Inheritance)

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java中PrintStream(列印輸出流)PrintStream 是列印輸出流,它繼承於FilterOutputStream。PrintStream 是用來裝飾其它輸出流。它能為其他輸出流添加了功能,使它們能夠方便地列印各種數據值表示形式。與其他輸出流不同,PrintStream 永遠不會拋出 ...
  • 最近做個發郵件的功能,需要將日誌文件通過郵件發送回來用於分析,但是日誌文件可能會超級大,測算下來一天可能會有800M的大小。所以壓縮是不可避免了,delphi中的預設壓縮演算法整了半天不太好使,就看了看7z,在windows下有dll那麼就用它吧。下載7z.dll,還有一個delphi的開發sdk文件...
  • 最近研究ehcache同步時發現一個問題:現有A、B兩個伺服器,由A伺服器向B伺服器同步信息,採用RMI方式手動方式進行同步配置信息如下: 同步的核心代碼:String key = StringUtils.leftPa...
  • 1、ArrayList非線程安全基於對象數組get(int index)不需要遍曆數組,速度快;iterator()方法中調用了get(int index),所以速度也快set(int index, E e)不需要遍曆數組,速度快add方法需要考慮擴容與數組複製問題,速度慢remove(Object...
  • 語言的數學運算和一些簡單的函數整理如下:向量可以進行那些常規的算術運算,不同長度的向量可以相加,這種情況下最短的向量將被迴圈使用。> x a x * a[1] 10 20 30 40> x + a[1] 11 12 13 14> sum(x) #對x中的元素求和[1] 10> prod(x) #.....
  • MyEclipse 快捷鍵1(CTRL)-------------------------------------Ctrl+1 快速修複Ctrl+D: 刪除當前行Ctrl+Q定位到最後編輯的地方Ctrl+L定位在某行Ctrl+O快速顯示 OutLineCtrl+T快速顯示當前類的繼承結構Ctrl+W...
  • 在上篇博客《基於C#的MongoDB資料庫開發應用(1)--MongoDB資料庫的基礎知識和使用》裡面,我總結了MongoDB資料庫的一些基礎信息,併在最後面部分簡單介紹了資料庫C#驅動的開發 ,本文繼續這個主體,重點介紹MongoDB資料庫C#方面的使用和封裝處理過程,利用泛型和基類對象針對數據訪...
  • 用過Reshaprer一段時間發現這個Visual Studio插件確實是個好東東,特別是神級快捷鍵Alt+Enter更是好用至極,可以解決大部分代碼問題,不過會發現裝上Reshaprer後VS自帶的快捷鍵就被打亂了,特別是喜歡的Ctrl+E,D設置文檔格式等,為了找回VS預設的快捷鍵盤於是重置了下...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...