zoukankan      html  css  js  c++  java
  • C#學習基礎繼承

    繼承

    任何面向對像的程序設計語言都必須提供兩個重要的特性:繼承性(inheritance)多態性(polymorphism).繼承的引入,就是在類之間建立一种相交關系,使得新定議的派生類的實例可以繼承已有的基類的特征和能力,而且可以加入新的特性或者是修改已有的特性,建立起類的層次.同一操作作用於不同的對像,可以有不同的解釋,產生不同的執行結果,這就是多態性.多態性通過派生類重載基類中的虛函數型方法來實現.

    注意:
    C#中,派生類只能從一個類中繼承.

    C#中,派生類從它的直接基類中繼承成員:方法,域,屬性,事件,索引指示器.
    除了構造函數和析構函數,派生類隱式地繼承了直接基類的所有成員.

    下面是一個關於車輛的例子
    using System;
    class Vehicle  //定義汽車類
    {
      int wheels;  //公有成員,輪子個數.
      protected float weight;  //保掮成員,重量.
      public Vehicle() {;}
      public Vehicle(int w,float g)
      {
        wheels=w;
        weight=g;
      }
      public void Speak()
      {
        Console.WriteLine("the w vehicle is speaking!");
      }
    }
    class Car:Vehicle  //定議轎車類,從汽車類中繼承
    {
      int passengers;  //私有成員,乘客數.
      public Car(int w,float g,int p):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
      }
    }
    Vehicle作為基類,體現了"汽車"這個實體有的公共性質;汽車都有輪子和重量,Car類繼承了Vehicle的這些性質,並且添加了自己身的特性:可以搭載乘客.

    覆蓋
    類的成員聲明中,可以聲明與繼而來的成員同名的成員.這時我們稱派生類的成員覆蓋

    (hide)了基類的成員.這种情況下,編譯器不會報告錯誤,但會給出一個警告.對派生類的成

    員使用new關鍵字,可以關閉這個警告.
    前面的汽車類的例子中,類Car繼承了Vehicle的Speak()方法.我們可以給Car類也聲明一個

    Speak()方法,覆蓋Vehicle中的Speak,見下面的代碼.
    using System;
    class Vehicle  //定義汽車類
    {
      int wheels;  //公有成員,輪子個數.
      protected float weight;  //保掮成員,重量.
      public Vehicle() {;}
      public Vehicle(int w,float g)
      {
        wheels=w;
        weight=g;
      }
      public void Speak()
      {
        Console.WriteLine("the w vehicle is speaking!");
      }
    }
    class Car:Vehicle  //定議轎車類,從汽車類中繼承
    {
      int passengers;  //私有成員,乘客數.
      public Car(int w,float g,int p):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
      }
      new public void Speak()
      {
        Console.WriteLine("Di-Di");
      }
    }
    注意:
    如果在成員聲明中加上了new關鍵字修飾,而該成員事實上並沒有覆蓋繼承的成員,編譯器將

    會給出警告,在一個成員聲明同時使用new和override則編譯器會報告錯誤.

    base保留字
    base關鍵字主要是為派生類調用基類成員提供一個簡寫的方法.我們先看一個例子程序代碼
    class A
    {
      public void F()
      {
      //.........
      }
      public int this[int nIndex]
      {
        get{};
        set{};
      }
    }
    class B:A
    {
      public void G()
      {
        int x=base[0];
        base.F();
      }
    }
    類B從類A中繼承,B的方法G中調用了A的方法F和索引指示器.方法F在進行編譯時等價於:
    public void G()
    {
      int x=(A (this))[0];
      (A (this)).F();
    }
    使用base關鍵字對基類成員的訪問格式為:
    base . indentifier
    base [expression-list]

    多態性

    在面向對像的系統中,多態性是一個非常重要的概念,它允許客戶對一個對像進行操作,由對

    像來完成一系列的動作,具體實現哪個動作,如何實現由系統負責解釋.

    C#支持兩種類型的多態性
    編譯時的多態性
    編譯時的多態性是通過重載來實現的.
    運行時的多態性
    運行時的多態性就是指直到系統運行時,才根據實際情況決定實現何种操作.C#中,運行時的

    多態性通過虛成員實現.

    編譯時的多態性為我們提供了運行速度快的特點,而運行時的多態多性則帶來了高度靈活和

    抽像的特點.

    虛方法
    當類中的方法聲明前加上了virtual修飾符,我們稱之為虛方法,反之為非虛.使用了virtual

    修飾符後,不允許再有static,abstract或override修飾符.
    對於非虛的方法,無論被其所在類的實例調用,還是被這個類的派生類的實例調用,方法的執

    行方式不變.而對於虛方法,它的執行方式可以被派生類改變,這种改變是通過方法的重載來

    實現的.
    下面的例子說明了虛方法與非using System;
    class A
    {
        public void F() { Console.WriteLine("A.F"); }
        public virtual void G() { Console.WriteLine("A.G"); }
    }
    class B : A
    {
        new public void F() { Console.WriteLine("B.F"); }
        public override void G() { Console.WriteLine("B.G"); }
    }
    class Test
    {
        static void Main()
        {
            B b = new B();
            A a = b;
            a.F();
            b.F();
            a.G();
            b.G();
        }
    }
    程序運行結果:
    A.F
    B.F
    B.G
    B.G
    例子中,A類提供了兩個方法;非虛的F和虛方法G.類B則提供了一個新的非虛的方法F(),從而

    覆蓋了繼承的F:類B同時還重載了繼承的方法G
    注意到本例中,方法a.G()實際調用了B.G,而不是A.G.這是因為編譯時值為A,但運行時值為

    B,所以B完成了對方法的實際調用.

    在派生類中對虛方法進行重載

    下面用汽車的例子來說明多態性的實現.
    using System;
    class Vehicle             //定義汽車類
    {
        public int wheels;  //公有成員;輪子個數
        protected float weight;//保護成員;重量
        public Vehicle(int w, float g)
        {
            wheels = w;
            weight = g;
        }
        public virtual void Speak()
        {
            Console.WriteLine("the w vehicle is speaking!");
        }
    }
    class Car : Vehicle  //定議轎車類
    {
        int passengers;  //私有成員
        public Car(int w, float g, int p)
            : base(w, g)
        {
            wheels = w;
            weight = g;
            passengers = p;
        }
        public override void Speak()
        {
            Console.WriteLine("The car is speaking:Di-di!");
        }
    }
    class Truck : Vehicle  //定議卡車類
    {
        int passengers;    //私有成員
        float load;        //私有成員
        public Truck(int w, float g, int p, float l)
            : base(w, g)
        {
            wheels = w;
            weight = g;
            passengers = p;
            load = l;
        }
        public override void Speak()
        {
            Console.WriteLine("The truck is speaking:Ba-ba!");
        }
    }
    class Test
    {
        public static void Main()
        {
            Vehicle v1 = new Vehicle(1,2);
            Car c1 = new Car(4, 2, 5);
            Truck t1 = new Truck(6, 5, 3, 10);
            v1.Speak();
            v1 = c1;
            v1.Speak();
            c1.Speak();
            v1 = t1;
            v1.Speak();
            t1.Speak();
        }
    }
    程序運行結果:
    the w vehicle is speaking!
    The car is speaking:Di-di!
    The car is speaking:Di-di!
    The truck is speaking:Ba-ba!
    The truck is speaking:Ba-ba!
    分析上面的例子,我們看到:
    Vehicle類中的Speak方法被聲明為虛方法,那麼在派生類中就可以重新定義此方法.
    在派生類Car和Truck中分別重載了Speak方法,派生類中的方法原型和基類中的方法原型必

    須完全一致.
    在Test類中,創建了Vehicle類的實例v1,並且先後指向Car類的實例c1和Truck類的實例t1.
    這里,Vehicle類的實例v1先後被賦予Car類的實例c1,以及Truck類的實例t1的值.在執行過

    程中,v1先後指代不同的類的實例,從而調用不同的版本.這里v1的Speak方法實現了多態性,

    並且v1.Speak()究竟執行哪個版本,不是在程序編譯時確定的,而是在程序的動態運行時,根

    據v1某一時刻的指代類型來確定的,所以還體現了動態的多態性.


    抽像與密封
    抽像類
    有時候,基類並不與具體的事物相聯系,而是只表達一种抽像的概念,用以為它的派生類提供

    了一個公共的界面.為此,C#中引入了抽像類(abstract class)的概念.
    抽像類使用abstract修飾符,對抽像類的使用有以下幾點規定:
    抽像類只能作為其它類的基類,它不能直接被實例化,而且對抽像類不能使用new操作符.抽

    像類如果含有抽像的變量或值,則它們要麼是null類型,要麼包含了對非抽像類的實例的引

    用.
    抽像類允許包含抽像成員,雖然這不是必須的.
    抽像類不能同時又是密封的.
    如果一個非抽像類從抽像類中派生,則其必須通過重載來實現所有繼承而來的抽像成員.請

    看下面的示例:
    abstract class A
    {
      public abstract void F();
    }
    abstract class B:A
    {
      public void G(){}
    }
    class C:B
    {
      public override void F(){}
    }
    抽像類A提供了一個抽像方法F.類B從抽像類A中繼承,並且又提供了一個方法G;因為B中並沒

    有包含對F的實現,所以B也必須是抽像類.類C從類B中繼承,類中重載了抽像方法F,並且提供

    了對F的具體實現,則類C允許是非抽像的.
    讓我們繼續研究汽車類的例子,我們從"交通工具"這個角度來理確Vehicle類的話,它應該表

    達一种抽像的概念,我們可以把它定議為抽像類,由轎車類Car和卡車類Truck來繼承這個抽

    像類,它們作為可以實例化的類.

    using System;
    abstract class Vehicle
    {
      public int wheels;
      protected float weight;
      public Vehicle(int w,float g)
      {
        wheels=w;
        weight=g;
      }
      public virtual void Speak()
      {
        Console.WriteLine("the w vehicle is speaking!");
      }
    }
    class Car:Vehicle
    {
      int passengers;
      public Car(int w,float g,int p):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
      }
      public override void Speak()
      {
        Console.WriteLine("the Car is speaking:Di-di!");
      }
    }
    class Truck:Vehicle
    {
      int passengers;
      float load;
      public Truck(int w,float g,int p,float l):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
        load=l;
      }
      public override void Speak()
      {
        Console.WirteLine("The truck is speaking:Ba-ba!");
      }
    }

    抽像方法
    具體的實現交給派生類通過重載來實現.
    一個方法聲明中如果加上了abstract修飾符,我們稱該方法為抽像方法(abstract method)
    如果一個方法被聲明也是抽像的,那麼該方法默認也是一個虛方法.事實上,抽像方法是一個

    新的虛方法,它不提供具體的方法實現代碼.非虛的派生類要求通過重載為繼承的虛方法提

    供自己的實現,而抽像方法則不包含具體的實現內容,所以方法聲明的執行體中只有一個分

    號";".
    只能在抽像類中聲明抽像方法.對抽像方法,不能再使用static或virtual修飾符,而且方法

    不能有任何可執行代碼,哪怕只是一對大括號中間加一個一個分號"{;}"都不允許出現,只需

    要給出方法的原型就可以了.
    "交通工具"的"鳴笛"這個方法實上是沒有什麼意義的,接下來我們利用抽像方法的概念繼續

    改寫這個類的例子:
    using System;
    abstract class Vehicle
    {
      public int wheels;
      protected float weight;
      public Vehicle(int w,float g)
      {
        wheels=w;
        weight=g;
      }
      public abstract void Speak();
    }
    class Car:Vehicle
    {
      int passengers;
      public Car(int w,float g,int p):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
      }
      public override void Speak()
      {
        Console.WriteLine("the Car is speaking:Di-di!");
      }
    }
    class Truck:Vehicle
    {
      int passengers;
      float load;
      public Truck(int w,float g,int p,float l):base(w,g)
      {
        wheels=w;
        weight=g;
        passengers=p;
        load=l;
      }
      public override void Speak()
      {
        Console.WirteLine("The truck is speaking:Ba-ba!");
      }
    }
    還要注意,抽像方法在派生類中不能使用base關鍵字來進行訪問.例如下面的寫法在編譯時

    會發生錯誤:
    class A
    {
      public abstract void F();
    }
    class B:A
    {
      public override void F()
      {
        base.F();  //錯誤,base.F是抽像方法
      }
    }
    我們還可以利用抽像方法來重載基類的虛方法,這時基類中的虛方法的執行代碼就被"撋截"

    了.下面的例子說明了這一點:
    class A
    {
      public virtual void F()
      {
        Console.WriteLine("A.F");
      }
    }
    abstract class B:A
    {
      public abstract override void F();
    }
    class C:B
    {
      public override void F()
      {
        Console.WriteLine("C.F");
      }
    }
    類A聲明了一個虛方法F,派生類B使用抽像方法重載了F,這樣B的派生類C就可以重載F並提供

    自己的實現.

    密封類
    密封類在聲明中使用sealed修飾符,這樣就可以防止該類被其它類繼承.如果試圖將一個密

    封類作為其它類的基類,C#將示出錯.再所當然,密封類不能同時又是抽像類,因為抽像總是

    希望被繼承的.
    在哪些場合下使用密封類呢?密封類可以阻止其它程序員在無意中繼承該類,而且密封可以

    起到運行時優化的效果.實際上,密封類中不可能有派生類,如果密封類實例中存在虛成員函

    數可以轉化為非虛的,函數修飾符virtual不再生效,看看下面這個例子.
    abstract class A()
    {
      public abstract void F();
    }
    sealed class B:A
    {
      public override void F()
      {
      /////////////////
      }
    }
    如果我們嘗試寫下面的代碼:
    class C:B{}
    C#會指出這個錯誤,告訴你B是一個密封類,不能試圖從B中派生任何類.
    密封方法
    C#還提出了密封方法(sealed method)的概念,以防止在方法所在類的派生類中對該方法的

    重載.對方法可以使用sealed修飾符,這時我們稱該方法是一個密封方法.不是類的每個成員

    方法都可以作為密封方法,密封方法必須對基類的虛方法進行重載,提供具體的實現方法,所

    以在方法的聲明中,sealed修飾符總和override修飾符同時使用.請看下面的例子:
    using System;
    class A
    {
      public virtual void F()
      {
        Console.WriteLine("A.F");
      }
      public virtual void G()
      {
        Console.WriteLine("A.G");
      }
    }
    class B:A
    {
      sealed override public void F()
      {
        Console.WriteLine("B.F");
      }
      override public void G()
      {
        Console.WriteLine("B.G");
      }
    }
    class C:B
    {
      override public void G()
      {
        Console.WriteLine("C.G");
      }
    }
    類B對基類A中的兩個虛方法均進行了重載,其中F方法使用了sealed修飾符,成為一個密封方

    法.G方法不是密碼方法,所以在B的派生類中,可以重載方法G,但不能重載方法F.


    繼承中關於屬性的一些問題

    和類的成員方法一樣,我們也可以定議屬性的重載,虛屬性,抽像屬性以及密封屬性的概念.與類和方法一樣,屬性的修飾符也應符合下列規則:

    從上面可以看出,屬性的這些規則與方法十分類似,對於屬性的訪問器,我們可以把get訪問

    器看成是一個與屬性修飾符相同,沒有參數,返回值為屬性的值類型的方法,把set訪問器看

    成是一個與屬性修飾符相同,僅含有一個value慘數,返回類型為void的方法.下面我們用客

    戶住宿的例子來說明屬性在繼承中的一些問題.
    using System;
    public enum sex
    {
      woman,
      man,
    };
    abstract public class People
    {
      private string s_name;
      public virtual string Name
      {
        get{return s_name;}
      }
      private sex m_sex;
      public virtual sex Sex
      {
        get{return m_sex;}
      }
      protected string s_card;
      public abstract string Card
      {
        get;set;
      }
    }
    上面的例子中聲明了"人"這個類,人的姓名Name和性別Sex是兩個只讀的虛屬性;身份證號

    Card是一個抽像屬性,允許讀寫.因為類People中包含了抽像屬性Card,所以People必須聲明

    是抽像的.下面我們為住宿的客人編寫一個類,類從Peple中繼承.
    class Customer:People
    {
      string s_no;
      int i_day;
      public string No
      {
        get {return s_no;}
        set
        {
          if(s_no!=value)
          {
            s_no=value;
          }
        }
      }
      public int Day
      {
        get {return i_day;}
        set
        {
          if(i_day!=value)
          {
            i_day=value;
          }
        }
      }
      public override string Name
      {
        get {return base.Name;}
      }
      public override sex Sex
      {
        get {return base.Sex;}
      }
      public override string Card
      {
        get {return s_card;}
        set {s_card=value;}
      }
    }
    在類Customer中,屬性Name,Sex和Card的聲明都加上了override修飾符,屬性的聲明都與基

    類People中保持一致.Name和Sex的get訪問器,Card的get和set訪問器都使用了base關鍵字

    來訪問基類People中的訪問器.屬性Card的聲明重載了基類People中抽像訪問器.這樣,在

    Customer類中沒有抽像成員的存在,Customer可以是非虛的.

     

    申明

    非源创博文中的内容均收集自网上,若有侵权之处,请及时联络,我会在第一时间内删除.再次说声抱歉!!!

    博文欢迎转载,但请给出原文连接。

  • 相关阅读:
    linux入门_韩顺平_复习版_用户管理
    linux入门_韩顺平_复习版_开启、重启、用户登陆注销
    python入门_老男孩_列表的增删查改
    git入门_尚硅谷_git简介
    码农要求_python豆瓣_2018校招
    python入门_老男孩_数据类型简介_int/bool/str转换_字符串索引和切片_字符串操作_day3
    LeetCode--链表3-经典问题
    LeetCode--链表2-双指针问题
    LeetCode--链表1-单链表
    LeetCode---二叉树3-总结例题
  • 原文地址:https://www.cnblogs.com/Athrun/p/946953.html
Copyright © 2011-2022 走看看