中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

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

發布時間:2020-07-08 21:28:45 來源:網絡 閱讀:1280 作者:yisuowushinian 欄目:編程語言

目錄:

【C#小知識】C#中一些易混淆概念總結--------數據類型存儲位置,方法調用,out和ref參數的使用

【C#小知識】C#中一些易混淆概念總結(二)--------構造函數,this關鍵字,部分類,枚舉

【C#小知識】C#中一些易混淆概念總結(三)--------結構,GC回收,靜態成員,靜態類

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

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

這次主要分享的內容是關于繼承的知識。

首先,我們先來看看繼承;

既然有繼承,就要有父類和子類,來看下面的一段代碼:

class Person
    {
        private int nAge;
        protected string strName;
        double douHeight;
        public string strEateType;
        public void Hello()
        {
            Console.WriteLine("我可以說Hello!");
        }
        public void Run()
        {
            Console.WriteLine("我可以跑!");
        }
    }
    class Student : Person
    {
    }

然后我在Main()函數中實例化子類的對象,代碼如下:

staticvoid Main(string[] args)        

{        

   Student stu1 = new Student();    

  }

那么在這個過程中內存中發生了些什么呢?

我們先來看misl的中間代碼,看看那能發現些什么


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

由此我們可以發現子類繼承了父類的所有成員包括Private和Protect,并為這些成員開辟了空間來存儲。

我們再來實例化我們的子類,然后訪問父類的字段和方法,會發現,如下的現象

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

所以雖然子類為父類的所有成員在堆中都開辟了空間,但是父類的私有成員(Private)子類訪問不到,

而受保護的成員(protected)可以通過實例化對象訪問的到。


所以在內存中的情況如下圖:

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

看下面的代碼,我們來探究一下在子類中this關鍵字和base關鍵字所訪問的類的成員有哪些,代碼如下:

class Student : Person
    {
        private string strClass;
        private string strAddress;
        public void Address(string cla, string adre)
        {
            //這里的this關鍵字調用了子類的成員和父類的非似有成員
            this.strClass = "五";
            this.strAddress = "北京";
            this.strName = "子強";
            //這里base關鍵字調用了是父類的非似有成員
            base.strName = "強子";
            Console.WriteLine("我是{0}年紀,來自{1}", cla, adre);
        }
        public void Sing()
        {
            this.strClass = "";
            Console.WriteLine("我可以唱歌!");
        }
    }

所以在子類中this關鍵字和base關鍵字的訪問范圍的示意圖如下:

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

二,關于子類對象的構造函數和父類構造函數的執行順序

我們分別為父類和子類添加顯式的構造函數,代碼如下

class Person
    {
        private int nAge;
        protected string strName;
        double douHeight;
        public string strEateType;
        //父類的構造函數
        public Person()
        {
            Console.WriteLine("我是父類的構造函數");
        }
        public void Hello()
        {
            Console.WriteLine("我可以說Hello!");
        }
        public void Run()
        {
            Console.WriteLine("我可以跑!");
        }
    }
    class Student : Person
    {
        private string strClass;
        private string strAddress;
        //子類的構造函數
        public Student ()
        {
            Console.WriteLine("我是子類的構造函數");
        }
    }

我們使用VS的單步調試,來看父類和子類顯式構造函數的執行順序,如下圖(動態圖片,可以看到過程):

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

很容易的可以發現,當創建子類對象的時候

①先調用了子類的構造函數

②調用了父類的構造函數

③執行了父類的構造函數

④執行了子類的構造函數


那么為什么會這樣呢?

我嘗試通過反編譯看源碼來解釋這個原因,但是反編譯的結果如下,

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

沒有發現有什么特別的地方可以解釋這個原因。


最后還是查閱微軟的MSDN官方文檔找到了答案(原文地址點擊這里)

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

根據微軟官方的代碼示例,那么下面的代碼的效果也是相同的

//子類的構造函數
        public Student ()
        {
            Console.WriteLine("我是子類的構造函數");
        }
//這里的代碼和上面的代碼效果是相同的
        public Student()
            :base()
        {
            Console.WriteLine("我是子類的構造函數");
        }

也就是說只要在子類顯式的聲明了無參的構造函數,在實例化子類的對象是,子類的無參構造函數都會去調用父類無參的構造函數。

那么,如果父類沒有這個無參的構造函數則會報錯。

如下面的代碼:

class Person
    {
        private int nAge;
        protected string strName;
        double douHeight;
        public string strEateType;
        //父類的構造函數
        //public Person()
        //{
        //    Console.WriteLine("我是父類的構造函數");
        //}
      //父類的有參數的構造函數,這里覆蓋了無參的構造函數
        public Person (string str)
        {
            Console.WriteLine("我是父類的構造函數{0}",str);
        }
        public void Hello()
        {
            Console.WriteLine("我可以說Hello!");
        }
        public void Run()
        {
            Console.WriteLine("我可以跑!");
        }
    }
    class Student : Person
    {
        private string strClass;
        private string strAddress;
        //子類的無參構造函數
        public Student ()
        {
            Console.WriteLine("我是子類的構造函數");
        }
        public Student(string strName)
        {
            Console.WriteLine("我的名字叫{0}",strName);
        }
    }


這時候編譯會報錯,

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

因為在父類中有參數的構造函數覆蓋了無參數的構造函數,所以在子類的無參數的構造函數沒辦法回調父類的無參數的構造函數初始化父類的成員變量。所以報錯。

那么在初始化子類的時候,為什么要調用父類的構造函數呢?

在初始化子類之前需要通過構造函數初始化父類的成員變量

父類的構造函數先于子類的構造函數執行的意義是什么呢?

當在父類的構造函數中和子類的構造函數中為父類的非私有成員變量賦不同默認值。當實例化子類,子類要調用構造函數初始化成員變量,如果先執行了子類的構造函數,再執行父類的構造函數,父類成員字段的值會覆蓋子類成員字段的值。但是我們想得到的是子類的屬性值。所以為了解決數據沖突,父類的構造函數要先于子類的構造函數執行。

如下面的代碼:

class Person
    {
        private int nAge;
        private string strName;
        double douHeight;
        public string strEateType;
       // 父類的構造函數
        public Person()
        {
            //再父類中對strEateType賦初始值
            this.strEateType = "吃飯";
            Console.WriteLine("我是父類的構造函數{0}", strEateType);
        }
    }
    class Student : Person
    {
        private string strClass;
        private string strAddress;
        //子類的構造函數
        public Student()
        {
            //在子類中對strEateType賦初始值
            this.strEateType = "吃面條";
            Console.WriteLine("我是子類的構造函數{0}",strEateType);
        }
    }

這時候我們通過,聲明子類對象訪問strEateType的值,如下:

Student stu1 = new Student();
            //stu1.
            string str = stu1.strEateType.ToString();
            Console.WriteLine(str);
            Console.ReadKey();

這里肯定是要打印出子類的屬性strEateType的值,如果先執行子類構造函數對strEateType賦值,然后父類的構造函數賦值覆蓋strEateType的初始值。那么打印出的將是父類成員字段的值。所以,父類的構造函數先于子類的構造函數執行。

打印結果如下:

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


三,子類是否可以有和父類的同名方法

看下面的代碼,我們聲明一個父類Person:


class Person
    {
        private int nAge;
        private string strName;
        double douHeight;
        public string strEateType;
       public  readonly string strrrr;
        // 父類的構造函數
        public Person()
        {
            this.strEateType = "吃飯";
            Console.WriteLine("我是父類的構造函數{0}", strEateType);
        }
        public Person(string str)
        {
            this.strName = str;
            Console.WriteLine("我是父類的構造函數{0}", str);
        }
        public void Hello()
        {
            Console.WriteLine("我可以說地球人的Hello!");
        }
        public void Run()
        {
            Console.WriteLine("我可以跑!");
        }
    }


聲明一個子類繼承Person,代碼如下:

class Worker:Person
    {
        public void  Hello()
        {
            Console.WriteLine("我是工人會說Hello!");
        }
        public new void  Run()
        {
            Console.WriteLine("我是工人我會奔跑!");
        }
    }


然后實例化Worker對象,打印Hello方法,結果如下圖:

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


這是為什么呢?編譯器已經告訴了我們,如下圖:

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


看出來是子類的方法隱藏了父類的方法。

既然子類可以定義和父類同名的方法,那么是否可以定同名的字段呢?答案是肯定的,而且會像同名方法一樣,子類同名字段會隱藏父類同名的字段


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

畢業實習交流群:221376964。你也可以關注我的新浪微博進行交流。

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


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

邵武市| 河曲县| 二手房| 万年县| 桓台县| 郧西县| 鄂托克旗| 皮山县| 九龙县| 井冈山市| 乌兰浩特市| 镇宁| 温州市| 昌宁县| 灌云县| 工布江达县| 佛山市| 中卫市| 天长市| 江北区| 赫章县| 图木舒克市| 九台市| 黄浦区| 梁平县| 信宜市| 丰顺县| 曲松县| 沅江市| 鹰潭市| 龙井市| 绥芬河市| 西和县| 遂溪县| 杨浦区| 伊吾县| 蓝山县| 津南区| 确山县| 垣曲县| 綦江县|