C#子類對基類方法的繼承、重寫與隱藏詳解
前言
提起子類、基類和方法繼承這些概念,肯定大家都非常熟悉。畢竟,作為一門支持OOP的語言,掌握子類、基類是學(xué)習(xí)C#的基礎(chǔ)。不過,這些概念雖然簡單,但是也有一些初學(xué)者可能會遇到的坑,我們一起看看吧。
子類繼承基類非私有方法
首先我們看最簡單的一種,子類繼承自基類,但子類對繼承的方法沒有任何改動
class Person { public void Greeting() { Console.WriteLine("Hello, I am Person"); } } class Employee : Person { } class Program { static void Main(string[] args) { Person p = new Employee(); p.Greeting(); } }
在這個例子中,作為子類的Employee自動繼承了基類的Greeting方法,當(dāng)在子類實例調(diào)用這個方法的時候,實際上調(diào)用的是基類的方法。這個例子非常簡單,毋庸多言。
子類覆蓋基類方法
接著是最常見的情況,子類覆蓋基類的方法,典型的例子如下
class Person { public virtual void Greeting() { Console.WriteLine("Hello, I am Person"); } } class Employee : Person { public override void Greeting() { Console.WriteLine("Hello, I am Employee"); } } class Program { static void Main(string[] args) { Employee e = new Employee(); Person p = e; p.Greeting(); e.Greeting(); } }
同樣,這段代碼也很簡單,基類方法通過關(guān)鍵字virtual表明方法可以被覆蓋,子類通過關(guān)鍵字override實現(xiàn)對基類方法的覆蓋,最后看調(diào)用部分,無論變量類型是子類還是基類,只要對象實際類型是子類,調(diào)用的方法都是子類覆蓋的方法,這也是多態(tài)的實現(xiàn)基礎(chǔ)。
子類隱藏基類方法
上面兩個例子都非常簡單,邏輯也很清楚,有點繞的要算子類隱藏基類方法的情況。
子類隱藏基類的非虛方法
基類被子類繼承的方法可能是虛方法,也可能是非虛方法,先看非虛方法被子類隱藏的情況,隱藏基類方法使用的關(guān)鍵字是new
class Person { public void Greeting() { Console.WriteLine("Hello, I am Person"); } } class Employee : Person { public new void Greeting() { Console.WriteLine("Hello, I am Employee"); } } class Program { static void Main(string[] args) { Employee e = new Employee(); Person p = e; p.Greeting(); e.Greeting(); } }
這里的結(jié)果可能就出乎某些初學(xué)者的意料了,為什么明明是子類Employee的實例,卻在不同的引用變量類型下呈現(xiàn)出了不一樣的效果?為什么會調(diào)用到了基類里面的方法?
其實這跟C#的函數(shù)調(diào)用機制有關(guān),一般來說,C#編譯成MSIL之后,有兩種函數(shù)調(diào)用方式。
- Call 以非虛的方式調(diào)用方法,一般用于靜態(tài)函數(shù)調(diào)用,因為靜態(tài)函數(shù)不可能是虛的,但也可以以非虛的方式調(diào)用一個虛方法
- Callvirt 以虛方式調(diào)用,一般用于非靜態(tài)方法和虛方法的調(diào)用。如果調(diào)用的方法非虛,則引用變量類型決定了最終調(diào)用的方法;反之,如果調(diào)用的方法為虛,則實例變量類型決定最終調(diào)用的方法——因為可能出現(xiàn)方法重寫,即,多態(tài)
用ILDASM打開我們的程序集看看,
證明了這里確實是用的Callvirt,而這個方法是非虛的方法,所以在兩次調(diào)用中,引用變量類型Person和Employee就能夠決定所調(diào)用的方法。兩個類分別實現(xiàn)了自己的Greeting方法,沒有出現(xiàn)子類覆蓋基類方法的情況。這就解釋了為什么兩次調(diào)用結(jié)果不同。最后讓我們來看看最復(fù)雜的一種情況
子類隱藏基類的虛方法
考慮下面的代碼
class Person { public virtual void Greeting() { Console.WriteLine("Hello, I am Person"); } } class Employee : Person { public new virtual void Greeting() { Console.WriteLine("Hello, I am Employee"); } } class Manager : Employee { public override void Greeting() { Console.WriteLine("Hello, I am Manager"); } } class Program { static void Main(string[] args) { Manager m = new Manager(); Person p = m; Employee e = m; p.Greeting(); e.Greeting(); m.Greeting(); } }
猜一下輸出應(yīng)該是什么?這也是老胡曾經(jīng)遇到過的一道筆試題,表面看著簡單,但是不注意也會掉坑里
1,2,3,答案揭曉
是不是有點出乎意料呢,讓我們來分析一下
首先,三次調(diào)用均是callvirt,而且方法Greeting是虛方法,我們需要考慮對象實例以決定要調(diào)用的方法。
- 在第一次調(diào)用中,引用變量類型是Person,雖然對象實例類型Manger重寫了Greeting方法,但是它重寫的是繼承自Manger基類Emplyee的Greeting方法,Person中Greeting方法在子類Manger中僅僅是被隱藏而沒有被重寫,所以這里調(diào)用的是Person中的Greeting
- 而第二次調(diào)用中,引用變量類型是Employee,Employee的Greeting方法被Manager重寫,所以這次調(diào)用到的是Manager中的Greeting
- 最后一次調(diào)用毋庸多言,簡單的重寫案例而已
怎么樣,是不是有小伙伴猜錯結(jié)果了?
總結(jié)
在子類對基類有方法繼承、重寫和隱藏的情況下,有時候判斷具體哪個方法被調(diào)用會有難度,但請記住以下要點:
如果被調(diào)用方法非虛,那么只用關(guān)注引用變量類型就好,引用變量類型能決定調(diào)用方法在哪里如果調(diào)用方法為虛,我們需要站在引用變量類型的角度,審視該方法是否被對象類型所重寫;若是,則調(diào)用對象類型的重寫方法;反之,則再次讓引用變量類型決定調(diào)用方法。
這樣,當(dāng)我們再遇到子類隱藏基類虛方法的情況,應(yīng)用以上要點就可以撥云見日。
到此這篇關(guān)于C#子類對基類方法的繼承、重寫與隱藏的文章就介紹到這了,更多相關(guān)C#子類對基類方法繼承、重寫與隱藏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# 并發(fā)控制框架之單線程環(huán)境下實現(xiàn)每秒百萬級調(diào)度
本文介紹了一款專為工業(yè)自動化及機器視覺開發(fā)的C#并發(fā)流程控制框架,通過模仿Go語言并發(fā)模式設(shè)計,支持高頻調(diào)度及復(fù)雜任務(wù)處理,已在多個項目中驗證其穩(wěn)定性和可靠性2024-10-10Unity3D實現(xiàn)鼠標(biāo)控制旋轉(zhuǎn)轉(zhuǎn)盤
這篇文章主要為大家詳細介紹了Unity3D實現(xiàn)鼠標(biāo)控制旋轉(zhuǎn)轉(zhuǎn)盤,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02C#中使用FilleStream實現(xiàn)視頻文件的復(fù)制功能
這篇文章主要介紹了C#中使用FilleStream實現(xiàn)視頻文件的復(fù)制功能,本文通過實例代碼給大家介紹的非常想詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09C#使用stackalloc分配堆棧內(nèi)存和非托管類型詳解
這篇文章主要為大家介紹了C#使用stackalloc分配堆棧內(nèi)存和非托管類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>2022-12-12Unity的IPreprocessBuild實用案例深入解析
這篇文章主要為大家介紹了Unity的IPreprocessBuild實用案例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05C#實現(xiàn)圖片上傳(PC端和APP)保存及 跨域上傳說明
這篇文章主要介紹了C#實現(xiàn)圖片上傳(PC端和APP)保存及 跨域上傳說明的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-12-12