溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

發(fā)布時間:2020-07-24 21:48:32 來源:網(wǎng)絡(luò) 閱讀:708 作者:yisuowushinian 欄目:編程語言


目錄:

【C#小知識】C#中一些易混淆概念總結(jié)--------數(shù)據(jù)類型存儲位置,方法調(diào)用,out和ref參數(shù)的使用

【C#小知識】C#中一些易混淆概念總結(jié)(二)--------構(gòu)造函數(shù),this關(guān)鍵字,部分類,枚舉

【C#小知識】C#中一些易混淆概念總結(jié)(三)--------結(jié)構(gòu),GC回收,靜態(tài)成員,靜態(tài)類

【C#小知識】C#中一些易混淆概念總結(jié)(四)---------解析Console.WriteLine()

C#小知識】C#中一些易混淆概念總結(jié)(五)---------深入解析C#繼承

----------------------------------分割線--------------------------------------

這一系列的文章在園子里還是比較受歡迎的。有一些留言指出了其中理論性的錯誤,怎么寫出來這些文章的,有沒有培訓(xùn)過等等問題。

下面就一并的回答這些問題吧。
1)自己今年六月份畢業(yè),現(xiàn)在在帝都實習(xí)。不過在學(xué)校已經(jīng)做過一些C#開發(fā)了,現(xiàn)在也是做.NET開發(fā)工作。

2)文章中很多知識是自己以前在網(wǎng)上下載的視頻教程,學(xué)習(xí)過程中所記的筆記。也就是在年前的時候,突然有一天發(fā)現(xiàn)自己的筆記本記了差不多塊一本了,之前也沒時間整理過,所以就想著把它們整理成博客文章,順便溫習(xí)一下這些筆記知識。

3)有園友問自己是不是在傳智培訓(xùn)過。首先說我沒有培訓(xùn)過,但是非常感謝傳智公開的一些自學(xué)教程。因為自己也是這些視頻的受益者,學(xué)到了很多知識,養(yǎng)成了一些好的學(xué)習(xí)習(xí)慣。

4)在整理筆記的過程中遇到了很多問題,其中自己參考了《C#本質(zhì)論》,《CLR via C#》還有就是MSDN的官方文檔。

3)不管怎樣還是會遇到一些自己解決不掉或者弄不清楚的問題,這個過程使用了Google搜索并和請教了一些園友。

4)錯誤總是會存在。謝謝看我博客的讀者你們的細(xì)心,指出了我博文中的錯誤。確定這些錯誤后,我都立即修改了自己的文章。

---------------------------------------------分割線-----------------------------------------------------

今天開始上班了。這幾天研究學(xué)習(xí)了一下思維導(dǎo)圖,感覺用它整理自己的知識非常的方便。所以,以后寫博客完成一個知識點,都會用思維導(dǎo)圖做一個總結(jié)。也能讓大家對所要讀的內(nèi)容有一個整體的把握。

我用的思維導(dǎo)圖軟件是FreeMind(免費的,但是得裝JDK),因為剛開始學(xué)習(xí)使用,很多操作技巧不是很熟練,做出來的導(dǎo)圖估計也不是很好,希望大家見諒。

首先,里氏替換原則。

這是理解多態(tài)所必須掌握的內(nèi)容。對于里氏替換原則維基百科給出的定義如下:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

為什么子類可以替換父類的位置,而程序的功能不受影響呢?

當(dāng)滿足繼承的時候,父類肯定存在非私有成員,子類肯定是得到了父類的這些非私有成員假設(shè),父類的的成員全部是私有的,那么子類沒辦法從父類繼承任何成員,也就不存在繼承的概念了)。既然子類繼承了父類的這些非私有成員,那么父類對象也就可以在子類對象中調(diào)用這些非私有成員。所以,子類對象可以替換父類對象的位置。

來看下面的一段代碼:

class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            Person p1 = new Student();
            Console.ReadKey();
        }
    }
    class Person
    {
    //父類的私有成員
    private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person構(gòu)造函數(shù),我是一個人!");
        }
        public void Say()
        {
            Console.WriteLine("我是一個人!");
        }
    }
    class Student : Person
    {
        public Student()
        {
            Console.WriteLine("我是Student構(gòu)造函數(shù),我是一個學(xué)生!");
        }
        public void SayStude()
        {
            Console.WriteLine("我是一個學(xué)生!");
        }
    }
    class SeniorStudent : Student
    {
        public SeniorStudent()
        {
            Console.WriteLine("我是SeniorStudent構(gòu)造函數(shù),我是一個高中生!");
        }
        public  void SaySenior()
        {
            Console.WriteLine("我是一個高中生!");
        }
    }

我們運行打印出的結(jié)果是:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

根據(jù)前面的構(gòu)造函數(shù)的知識很容易解釋這個結(jié)果。那么我們在Main()函數(shù)中添加如下的代碼:

static void Main(string[] args)
        {
            Person p = new Person();
            p.Say();
  
            Person p1 = new Student();
            p1.Say();
            Console.ReadKey();
        }

在訪問的過程中,可以發(fā)現(xiàn)p只可以訪問父類的say

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

而p1也只可以訪問父類的Say方法

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

其實在上面的代碼中,就滿足了里氏替換原則。子類的Student對象,替換了父類Person對象的位置。


那么它們在內(nèi)存中發(fā)生了些什么呢?如下圖:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

由上可以知道,當(dāng)一個父類的變量指向一個子類對象的時候只能通過這個父類變量調(diào)用父類成員,子類獨有的成員無法調(diào)用。

同理我們可以推理出,子類的變量是不可以指向一個父類的對像的

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

但是當(dāng)父類變量指向一個子類變量的時候,可以不可以把父類的變量轉(zhuǎn)化成子類的對象呢?看下圖

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

關(guān)于引用類型的兩種轉(zhuǎn)換方式:

由上面的代碼我們已經(jīng)知道了一種轉(zhuǎn)換,就是在變量錢直接加需要轉(zhuǎn)換的類型,如下代碼:

Student s2 = (Student)p1;

那么第二種轉(zhuǎn)換方式就是使用as關(guān)鍵字,如下代碼:

//將指向子類對象的變量轉(zhuǎn)化成子類類型
Student s2 = (Student)p1;            
//使用as關(guān)鍵字,轉(zhuǎn)換失敗返回一個null值
Student s3 = p1 as Student;

使用as關(guān)鍵字和第一種強制轉(zhuǎn)換的區(qū)別就是,第一種如果轉(zhuǎn)換失敗會拋異常,第二種轉(zhuǎn)換失敗則返回一個null值。

思維導(dǎo)圖總結(jié)如下:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法


二,虛方法

使用virtual關(guān)鍵字修飾的方法,叫做虛方法(一般都是在父類中)。

看下面的一段代碼:


class Person
    {
        private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person構(gòu)造函數(shù),我是一個人!");
        }
        //這里定義了一個虛方法
        public virtual void Say()
        {
            Console.WriteLine("我是一個人!");
        }
    }
    class Student : Person
    {
        //子類使用override關(guān)鍵字改寫了父類的虛方法
        public override void Say()
        {
            Console.WriteLine("我是一個學(xué)生!");
        }
        public Student()
        {
            Console.WriteLine("我是Student構(gòu)造函數(shù),我是一個學(xué)生!");
        }
        public void SayStude()
        {
            Console.WriteLine("我是一個學(xué)生!");
        }
    }


緊接著在main()函數(shù)中添加如下的代碼:

static void Main(string[] args)
        {
            Person p = new Person();
            p.Say();
            Person p1 = new Student();
            p1.Say();
            Student s = new Student();
            s.Say();
            Console.ReadKey();
        }

打印結(jié)果如下:


【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法


我們很明顯的可以發(fā)現(xiàn),第二個表達(dá)式滿足里氏替換原則,p1.Say()執(zhí)行的應(yīng)該是父類的Say()方法,但是這里卻執(zhí)行了子類的Say()方法。

這就是子類使用override關(guān)鍵字的Say()方法覆蓋了父類的用Virtual關(guān)鍵字修飾的Say()方法。

我們使用動態(tài)圖片看一下調(diào)試過程,

①首先是沒有使用任何關(guān)鍵字:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法


由上可以看出直接跳入父類,執(zhí)行了父類的Say()方法;


②再看使用virtual和override關(guān)鍵字的動態(tài)調(diào)試圖片,如下

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法

可以看到直接到子類去執(zhí)行override關(guān)鍵字修飾的Say()方法。


那么如果父類使用virtual關(guān)鍵字修飾,而子類沒有重寫該方法時會怎么樣呢?如下面的代碼:


class Program
    {
        static void Main(string[] args)
        {
               
            Person p1 = new Student();
            p1.Say();
            Console.ReadKey();
        }
    }
    class Person
    {
        private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person構(gòu)造函數(shù),我是一個人!");
        }
        //這里定義了一個虛方法
        public virtual void Say()
        {
            Console.WriteLine("我是一個人!");
        }
    }
    class Student : Person
    {
        //子類中沒有出現(xiàn)override關(guān)鍵字修飾的方法
        public void SayStude()
        {
            Console.WriteLine("我是一個學(xué)生!");
        }
    }


執(zhí)行結(jié)果如下:


【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法



所以,如果子類找不到override方法,則會回溯到該子類的父類去找是否有override方法,知道回溯到自身的虛方法,并執(zhí)行。

虛方法知識總結(jié)的思維導(dǎo)圖如下:

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法


如果您覺得不錯,點擊右下角贊一下吧!您的支持,是我寫作的動力!

畢業(yè)實習(xí)交流群:221376964。你也可以關(guān)注我的新浪微博進行交流。

【C#小知識】C#中一些易混淆概念總結(jié)(六)---------解析里氏替換原則,虛方法


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI