C# 多態的實現

来源:http://www.cnblogs.com/ldyblogs/archive/2017/11/08/override.html
-Advertisement-
Play Games

封裝、繼承、多態,面向對象的三大特性,前兩項理解相對容易,但要理解多態,特別是深入的瞭解,對於初學者而言可能就會有一定困難了。我一直認為學習OO的最好方法就是結合實踐,封裝、繼承在實際工作中的應用隨處可見,但多態呢?也許未必,可能不經意間用到也不會把它跟“多態”這個詞對應起來。在此拋磚引玉,大家討論 ...


封裝、繼承、多態,面向對象的三大特性,前兩項理解相對容易,但要理解多態,特別是深入的瞭解,對於初學者而言可能就會有一定困難了。我一直認為學習OO的最好方法就是結合實踐,封裝、繼承在實際工作中的應用隨處可見,但多態呢?也許未必,可能不經意間用到也不會把它跟“多態”這個詞對應起來。在此拋磚引玉,大家討論,個人能力有限,不足之處還請指正。
之前看到過類似的問題:如果面試時主考官要求你用一句話來描述多態,儘可能的精煉,你會怎麼回答?當然答案有很多,每個人的理解和表達不盡相同,但我比較趨向這樣描述:通過繼承實現的不同對象調用相同的方法,表現出不同的行為,稱之為多態。

例1:

代碼 
public class Animal
    {
        public virtual void Eat()
        {
            Console.WriteLine("Animal eat");
        }
    }

    public class Cat : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Cat eat");
        }
    }

    public class Dog : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Dog eat");
        }
    }

    class Tester
    {
        static void Main(string[] args)
        {
            Animal[] animals = new Animal[3];

            animals[0] = new Animal();
            animals[1] = new Cat();
            animals[2] = new Dog();

            for (int i = 0; i < 3; i++)
            {
                animals[i].Eat();
            }
        }
    }

輸出如下:

Animal eat...

Cat eat...

Dog eat...

在上面的例子中,通過繼承,使得Animal對象數組中的不同的對象,在調用Eat()方法時,表現出了不同的行為。

多態的實現看起來很簡單,要完全理解及靈活的運用c#的多態機制,也不是一件容易的事,有很多需要註意的地方。

 

1. new的用法

先看下麵的例子。

例2

 

代碼 
public class Animal
    {
        public virtual void Eat()
        {
            Console.WriteLine("Animal eat");
        }
    }

    public class Cat : Animal
    {
        public new void Eat()
        {
            Console.WriteLine("Cat eat");
        }
    }

    class Tester
    {
        static void Main(string[] args)
        {
            Animal a = new Animal();
            a.Eat();

            Animal ac = new Cat();
            ac.Eat();

            Cat c = new Cat();
            c.Eat();
        }
    }

運行結果為:

Animal eat...

Animal eat...

Cat eat...

可以看出,當派生類Cat的Eat()方法使用new修飾時,Cat的對象轉換為Animal對象後,調用的是Animal類中的Eat()方法。其實可以理解為,使用new關鍵字後,使得Cat中的Eat()方法和Animal中的Eat()方法成為毫不相關的兩個方法,只是它們的名字碰巧相同而已。所以, Animal類中的Eat()方法不管用還是不用virtual修飾,也不管訪問許可權如何,或者是沒有,都不會對Cat的Eat()方法產生什麼影響(只是因為使用了new關鍵字,如果Cat類沒用從Animal類繼承Eat()方法,編譯器會輸出警告)。

 

我想這是設計者有意這麼設計的,因為有時候我們就是要達到這種效果。嚴格的說,不能說通過使用new來實現多態,只能說在某些特定的時候碰巧實現了多態的效果。

 

 

2.override實現多態

真正的多態使用override來實現的。回過去看前面的例1,在基類Animal中將方法Eat()用virtual標記為虛擬方法,再在派生類Cat和Dog中用override對Eat()修飾,進行重寫,很簡單就實現了多態。需要註意的是,要對一個類中一個方法用override修飾,該類必須從父類中繼承了一個對應的用virtual修飾的虛擬方法,否則編譯器將報錯。

 

好像講得差不多了,還有一個問題,不知道你想沒有。就是多層繼承中又是怎樣實現多態的。比如類A是基類,有一個虛擬方法method()(virtual修飾),類B繼承自類A,並對method()進行重寫(override修飾),現在類C又繼承自類B,是不是可以繼續對method()進行重寫,並實現多態呢?看下麵的例子。

 

例3:

 

代碼 
public class Animal
    {
        public virtual void Eat()
        {
            Console.WriteLine("Animal eat");
        }
    }

    public class Dog : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Dog eat");
        }
    }

    public class WolfDog : Dog
    {
        public override void Eat()
        {
            Console.WriteLine("WolfDog eat");
        }
    }

    class Tester
    {
        static void Main(string[] args)
        {
            Animal[] animals = new Animal[3];

            animals[0] = new Animal();
            animals[1] = new Dog();
            animals[2] = new WolfDog();

            for (int i = 0; i < 3; i++)
            {
                animals[i].Eat();
            }
        }
}

 運行結果為:

Animal eat...

Dog eat...

WolfDog eat... 

在上面的例子中類Dog繼承自類Animal,對方法Eat()進行了重寫,類WolfDog又繼承自Dog,再一次對Eat()方法進行了重寫,並很好地實現了多態。不管繼承了多少層,都可以在子類中對父類中已經重寫的方法繼續進行重寫,即如果父類方法用override修飾,如果子類繼承了該方法,也可以用override修飾,多層繼承中的多態就是這樣實現的。要想終止這種重寫,只需重寫方法時用sealed關鍵字進行修飾即可。

 

 

3. abstract-override實現多態

先在我們在來討論一下用abstract修飾的抽象方法。抽象方法只是對方法進行了定義,而沒有實現,如果一個類包含了抽象方法,那麼該類也必須用abstract聲明為抽象類,一個抽象類是不能被實例化的。對於類中的抽象方法,可以再其派生類中用override進行重寫,如果不重寫,其派生類也要被聲明為抽象類。看下麵的例子。

例4:

 

 

代碼 
public abstract class Animal
    {
      public abstract void Eat();
    }

    public class Cat : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Cat eat");
        }
    }

    public class Dog : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Dog eat");
        }
    }

    public class WolfDog : Dog
    {
        public override void Eat()
        {
            Console.WriteLine("Wolfdog eat");
        }
    }

    class Tester
    {
        static void Main(string[] args)
        {
            Animal[] animals = new Animal[3];

            animals[0] = new Cat();
            animals[1] = new Dog();
            animals[2] = new WolfDog();

            for (int i = 0; i < animals.Length; i++)
            {
                animals[i].Eat();
            }
        }
    }

運行結果為:

Cat eat...

Dog eat...

Wolfdog eat...

從上面可以看出,通過使用abstract-override可以和virtual-override一樣地實現多態,包括多層繼承也是一樣的。不同之處在於,包含虛擬方法的類可以被實例化,而包含抽象方法的類不能被實例化。

以上動圖由“圖鬥羅”提供


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

-Advertisement-
Play Games
更多相關文章
  • 目的:1 學習mvc+ef 2 寫下日記也是對自己的督促 第0課 從0開始 ASP.NET MVC開發模式和傳統的WebForm開發模式相比,增加了很多"約定"。 直接講這些 "約定" 會讓人困惑,而且東西太多容易忘記。 和微軟官方教程不同,筆者儘量不用腳手架,從空白框架開始,一步一步添加功能,每次 ...
  • 提取文本的情況在工作和學習中常會遇到,在前面的文章中,已經講述瞭如何提取PPT中文本框里的文本,在本篇文章中,將介紹如何使用C#代碼語言提取PPT文檔中SmartArt和批註中的文本。同樣的,程式裡面需要使用到Spire.PPT for .NET,在編寫代碼前,需先安裝,並添引用dll文件到項目程式 ...
  • 新開一個Web site。沒有使用jQuery,當Insus.NET使用一些驗證控制項時,如RequiredfieldValidator,程式出現下麵錯誤: WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for ...
  • Dapper使用技巧和基礎CRUD 一、使用模型的增刪改查。 2.建立資料庫 3.數據增刪改查 引用比較亂,做了一些測試,順便說說MongoDB.Driver也很好用,下次寫。 備註:傳入參數可以直接用模型,也可以用object new {Name="test",Code="Test"};這是我寫這 ...
  • 問題 由於在初學c#的時候 使用了 線程委托去執行函數,是為了不讓軟體窗體假死。所以使用下方代碼: Thread th = new Thread(Getform); //創建線程 th.Start(); 在使用前需要引入 : using System.Threading; 但是,在Getform 函 ...
  • .NET中的線程池是受CLR管理的,TheadTool類有一個QueueUserWorkItem靜態方法,這個靜態方法接受一個委托,代表用戶自定義的一個非同步操作,在這個方法被調用之後,委托會進入到內部隊列中,如果池中沒有線程,則創建一個工作線程,把第一個委托放入工作線程。如果繼續放入委托,則池創建新... ...
  • fileStream:操作位元組的,也就是所有的文件都可以拿它去操作 / file / path / streamRead / streamWrite(這兩個都是操作字元的,它所操作的都是文本文件) fileStream 與file的區別,fileStream可以操作大文件,因為fileStream是 ...
  • 返回目錄 應該這樣理解它 非同步,早期開發人員對它有很多誤解,認為不阻塞主線程就是非同步,更有認為不阻塞UI就是非同步,但非同步歸根結底和這兩個東西關係並不大,非同步的出現主要是為了提高線程的利用率,讓可用線程更高,而不是一個線程只做一件事,這件事沒有完成就不去做下麵的事情,這是不正確的,線程應該被解放出來! ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...