細說C#繼承

簡介編程

繼承(封裝、多態)是面向對象編程三大特性之一,繼承的思想就是擯棄代碼的冗餘,實現更好的重用性。函數

繼承從字面上理解,無外乎讓人想到某人繼承某人的某些東西,一個給一個拿。這個語義在生活中,就像this

家族繼承財產,爺爺將財產繼承給兒女,兒女在將財產繼承給子孫,有些東西能夠繼承有些的東西只繼承給spa

某人。映射到編程當中,其思想也大體如此。調試


 經過示例引出繼承的做用code

在代碼中定義個三個類:Cat貓、Dog狗、Cattle牛。對象

從類圖上能夠看出紅色標識區域,三個類的定義出現了大量的冗餘(字段、屬性、方法),那麼在編寫代碼時就會出現大量的重複代碼。blog

試想一下,隨着業務功能的擴展,可能會出現更多類,那麼冗餘(重複的代碼)會更多。好比出現一樣會形成冗餘的類:繼承

Pig豬、Panda熊貓、Sheep羊......等等。這些類一樣會有相同的特徵:名稱、性別、年齡、奔跑(字段、屬性、方法)。get


 如何解決此類冗餘問題 —— 使用繼承

繼承的思想:

當咱們定義了多個類,這多個類都存在重複的成員(共性)。咱們能夠將這些重複的成員單獨的提取封裝到一個類中,做爲這些具備相同特徵類的父類。

將此思想做用於上述的三個類

提取共性:能夠直觀看出重複的具備共性的項目有:1.字段和屬性(年齡、姓名、性別)、2.方法(奔跑)。

封裝到一個類:如何定義這個類?Cat貓、Dog狗、Cattle牛有明顯共同的特性,就是他們都是動物,故能夠抽象定義一個Animal動物類。

 

如何在代碼中實現繼承

    class Animal
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private string gender;
        public string Gender
        {
            get { return gender; }
            set { gender = value; }
        }
        
        private int age;
        public int Age
        {
            get { return age; }
            set { age = value; }
        }
        
        public void Run()
        {
            Console.WriteLine("奔跑。。。");
        }

    }

    class Cat:Animal
    {   
        public void CatchMouse()
        {
            Console.WriteLine("抓老鼠。。。");
        }
    }

    class Dog:Animal
    {
        public void GuardHouse()
        {
            Console.WriteLine("看家護院。。。");
        }
    }

    class Cattle:Animal
    {
        public void Plowland()
        {
            Console.WriteLine("耕田。。。");
        }
    }

經過一個簡單的  :(冒號)實現了繼承關係。

實現繼承後產生了兩個角色:1.子類(派生類)、2.父類(基類)

代碼中子類刪除父類提取的重複性成員。

 

實現繼承後的關係以下圖:

實現繼承後每一個子類僅保留了本身特有的特性,大大減小了冗餘。

 

繼承後的能力

子類的共性成員都被父類提取了,那麼子類要使用怎麼辦?

子類繼承父類後,將會隱式繼承父類的全部成員,但不包括構造函數。

在繼承後,訪問其父類成員,會受到訪問修飾符的限制。故,修飾爲private的私有成員不會訪問到。

 

繼承的特性

1.繼承的單根性:

  一個子類只能有一個父類,就比如一我的只有一個父親。

2.繼承的傳遞性:  

   例如, ClassC 派生自 ClassB,而且 ClassB 派生自 ClassA,則 ClassC 會繼承在 ClassB 和 ClassA 中聲明的成員。

依次順序能夠不斷向上取。

圖例:


 繼承被後的祕密 —— 子類和父類的構造函數(難點)

給父類編寫了一個構造函數,示例代碼以下:

 1     class Animal
 2     {
 3         public Animal(string name,string gender,int age)
 4         {
 5             this.Name = name;
 6             this.Gender = gender;
 7             this.Age = age;
 8         }
 9 
10         private string name;
11         public string Name
12         {
13             get { return name; }
14             set { name = value; }
15         }
16 
17         private string gender;
18         public string Gender
19         {
20             get { return gender; }
21             set { gender = value; }
22         }
23         
24         private int age;
25         public int Age
26         {
27             get { return age; }
28             set { age = value; }
29         }
30         
31         public void Run()
32         {
33             Console.WriteLine("奔跑。。。");
34         }
35 
36         private void ri()
37         { }
38 
39     }
40 
41     class Cat:Animal
42     {   
43         public void CatchMouse()
44         {
45             Console.WriteLine("抓老鼠。。。");
46         }
47     }
48 
49     class Dog:Animal
50     {
51         public void GuardHouse()
52         {
53             Console.WriteLine("看家護院。。。");
54         }
55     }
56 
57     class Cattle:Animal
58     {
59         public void Plowland()
60         {
61             Console.WriteLine("耕田。。。");
62         }
63     }

 

嘗試運行:

爲何會提示報這個錯誤?意思說父類不能沒有一個無參的構造函數。

學過構造函數的應該都會知道,類在沒有指定任何構造函數的狀況下,程序默認會指派一個無參的構造函數。

上述的例子因爲咱們手動添加的那個構造函數,默認的構造函數就被清除掉了。

在暫且不知道緣由的狀況下,咱們嘗試補全那個無參的構造函數,在進行生成代碼,此時編譯經過沒有報錯。

 

根據此特徵咱們能夠推測子類和父類的構造函數必定有關係,但必定不是繼承關係

 

嘗試調用剛剛定義的父類無參構造函數,在調用列表並無顯示,只顯示了類自身的一個無參構造函數。

證實了子類不能繼承父類的構造函數。

 

經過調試代碼監視子類實例化對象的過程,看它到底和父類的構造函數發生了什麼。

經過調試發如今建立子類對象時的代碼執行邏輯以下:

子類會首先去默認執行父類的無參構造函數,而後在執行本身的構造函數

這條定論就很好的解釋了,爲何在上述例子爲何會出現的錯誤。可是子類又爲何要先去執行父類的構造函數?

解釋:

由於子類繼承了父類的成員,這一項描述只能說明子類擁有的權利,並不表明子類去執行了。

在原則上要使用類的成員,必需要經過類的實例對象去調用。因此子類要調用到父類的成員,就必須去經過調用

父類的構造函數,在子類的內部建立一個父類的對象,以便本身去調用父類的成員。

 

總結:

子類始終要使用父類的一個構造函數在本身內部建立一個父類對象,爲了調用父類的成員。

子類默認調用父類的無參構造函數,因此在顯示編寫一個有參構造函數時致使父類沒有了無參構造函數,從而編譯出錯。


 在子類中使用顯示調用父類構造函數

 

做用1:

提升代碼重用性,子類無需在類中定義,直接使用父類的。

做用2:

上述例子講過子類在實例化對象時會調用父類的默認無參構造函數,由於子類的目的就是經過父類構造函數建立一個對象。

經過這樣顯示的調用,那麼在父類有沒有無參構造函數都沒什麼關係了。


子類中存在和父類中相同的成員

示例:

根據VS給咱們提示的消息,咱們能夠看出,當代碼中存在子類的成員和父類的成員相同的時候,子類的成員將父類的成員隱藏了。

隱藏事後子類將沒法訪問到父類的成員。若是是刻意爲之,咱們可使用new 關鍵字顯示的說明,從而提升可讀性。

指定new關鍵字:

 

此時提示的波浪線已消除。


 其餘注意點

在C#中,全部的類都直接或間接的繼承自object類(當咱們定義一個類的時候,若是沒有給該類指定繼承一個類,那麼這個類就繼承了object類)。

相關文章
相關標籤/搜索