【Unity|C#】基礎篇(3)——類(class)/ 接口(interface)


【學習資料】

  《C#圖解教程》(第4~7章)https://www.cnblogs.com/moonache/p/7687551.html
  電子書下載:https://pan.baidu.com/s/1mhOmBG0

 

【內容】 

    • 所有類的基類
    • 類的組成
    • 類的實例化、內存分配
    • 類的特性:封裝、繼承、多態
    • 接口
    • 擴展知識
      • 隱藏基類的成員
      • struct、class
      • 抽象函數(abstract)、虛函數(virtual)
      • 抽象類(abstract)、接口(interface)

 


 【筆記】

  所有類的基類: object 

  類由2部分組成

    > 數據成員:存儲與類或類的實例相關的數據
    > 函數成員:執行代碼

      

  類的實例化

    > 聲明一個類的引用,指向空數據null

       

    > new數據部分,並將引用指向數據

      

 


 【類的特性】

  類的三大特性:封裝、繼承、多態

    > 封裝

      > 訪問修飾符

    > 繼承

      > 派生子類

      > 構造/析構函數

    > 多態

      > 靜態多態性:函數重載(不同參數)、運算符重載

      > 動態多態性:虛函數、抽象函數、隱藏方法

 

  • 封裝

     

 

  • 繼承
    • this   當前類對象
    • base 父類對象
    • sealed密封類,不能被繼承
    • sealed class Entity
      {
      }
      class Car : Entity // sealed不能被繼承,會報錯
      {
      }
    • C#不支持類的多繼承,但可以繼承多個接口(interface)
    • 父類初始化:在初始化列表里進行
    • public Tabletop(double l, double w) : base(l, w)
      { }
    • 構造函數
      • 在創建類對象時,會執行構造函數:Car car = new Car()
      • 構造順序:調用父類的構造,調用子類
    • 析構函數
      • 在釋放對象時(GC垃圾回收),會執行析構函數
      • 析構順序:調用子類的析構,調用父類
    • class Entity
      {
          // 默認構造函數,無參數
          public Entity()
          {
              Debug.Log("new Entity");
          }
          // 帶參構造函數
          public Entity(int id)
          {
              Debug.Log("new Entity id=" + id);
          }
          // 重寫析構函數,一個類只能有一個,且無參數
          ~Entity()
          {
              Debug.Log("delete Entity");
          }
      }
      class Car : Entity // sealed不能被繼承,會報錯
      {
          // 不寫父類的構造,默認調用默認構造函數Entity()
          public Car()
          {
              Debug.Log("new Car");
          }
          // 在初始化列表: 調用父類的帶參構造函數
          public Car(int id) 
              : base(id)
          {
              Debug.Log("new Car");
          }
          // 重寫析構函數,一個類只能有一個,且無參數
          ~Car()
          {
              Debug.Log("delete Car");
          }
      }

 

  • 多態
    • 靜態多態性

      • 函數重載(不同參數)

      • public Tabletop(double l, double w) : base(l, w)
        { }
      • 運算符重載
      • public static Box operator +(Box b, Box c)
        { }

 

    • 動態多態性:子類重寫父類方法
      • 虛函數
        • 關鍵字:virtual /  override
        • 子類重寫虛函數后:不管在父類中調用,還是在子類中調用,執行的都是子類重寫后的函數 (Car.Move)
        • 調用父類虛函數的方法:通過 base 關鍵字調用(base.Move()
        • 注:子類也可不重寫父類的虛函數
        • class Entity
          {
              public void AI()
              {
                  Move();
              }
              public virtual void Move() // 虛函數聲明 virtual
              {
                  Debug.Log("Entity.Move");
              }
          }
          class Car : Entity
          {
              // 重寫方法
              public override void Move() // 重寫虛函數 override
              {
                  Debug.Log("Car.Move");
              }
          }

          // 測試Test
          void Start() { Entity entity = new Car(); Car car = (Car)entity; entity.Move(); // 輸出:Car.Move car.AI(); // 輸出:Car.Move }

 

      •  抽象函數
        • 關鍵字:abstract / override
        • 注:父類只聲明抽象函數,沒有具體實現
        • 注:存在抽象函數的類,必須聲明為抽象類,且不能實例化成對象
        • // 含有抽象函數,必須聲明為抽象類,且不能實例化對象
          abstract class Entity
          {
              public abstract void Move(); // 抽象函數
          }
          class Car : Entity
          {
              // 子類必須實現父類的抽象函數
              public override void Move()
              {
                  Debug.Log("Car.Move");
              }
          }
          
          void Start()
          {
              // 報錯,抽象類無法實例化
              //Entity entity2 = new Entity();
          
              Car entity = new Car();
              entity.Move();  // 輸出:Car.Move
          }

 

      • 隱藏方法 (不推薦,容易出錯)
        • 子類重寫父類虛函數時,不寫 override 關鍵字編譯器也會顯示警告(warning)
        • 注:具體執行父類/子類函數,根據調用環境決定(指向對象的引用類型、父類or子類其他函數中進行調用)
        • 測試1:不同引用類型調用Move 、通過AI()函數中調用
          • class Entity
            {
                public void AI()
                {
                    Move();
                }
                public virtual void Move()
                {
                    Debug.Log("Entity.Move");
                }
            }
            class Car : Entity
            {
                // 不寫override
                public void Move()
                {
                    Debug.Log("Car.Move");
                }
            }
            
            void Start()
            {
                Entity entity = new Car();
                Car car = (Car)entity;
                entity.Move();  // 輸出:Entity.Move
                entity.AI();    // 輸出:Entity.Move
                car.Move();     // 輸出:Car.Move
                car.AI();       // 輸出:Entity.Move
            }
        •  測試2:子類重寫AI()函數
          • class Entity
            {
                public virtual void AI()
                {
                    Move();
                }
                public virtual void Move()
                {
                    Debug.Log("Entity.Move");
                }
            }
            class Car : Entity
            {
                public override void AI()
                {
                    Move();
                }
                // 不寫override
                public void Move()
                {
                    Debug.Log("Car.Move");
                }
            }
            
            void Start()
            {
                Entity entity = new Car();
                Car car = (Car)entity;
                entity.Move();  // 輸出:Entity.Move
                entity.AI();    // 輸出:Car.Move
                car.Move();     // 輸出:Car.Move
                car.AI();       // 輸出:Car.Move
            }

 


 【接口】 

  > 關鍵字:interface

  > 相當於是個規則,里面只能有:方法、屬性、索引、事件

  > 一個類只能繼承一個父類,但可以實現多個接口 

  > 注:接口 也可以 繼承另一個 接口

  > 注:一個類繼承了接口后,接口中所有的方法(包括屬性、索引、事件)都必須實現

  > 注:實現接口中的方法必須定義為 public

    interface EntityInterface
    {
        int Value { get; set; } // 可以聲明屬性,但子類中必須實現
        void Move();            // 不用寫修飾符(public)

        //int value;            // 不能定義變量
        //void AI() { Move(); } // 不能實現接口函數
    }
    class Car : EntityInterface
    {
        // 必須實現接口中的屬性,且必須為 public
        public int Value
        {
            get;
            set;
        }
        // 必須實現接口中的函數,且必須為 public
        public void Move()
        {
        }
    }

 


 【擴展知識】

  • 隱藏基類的成員
    • 子類通過new關鍵字,隱藏父類的成員數據、方法
    • 具體調用的是哪個?根據調用環境決定(指向對象的引用類型、父類or子類其他函數中進行調用)

        

    •  案例1:不同引用類型獲取.value、調用父類的PrintValue
      • class A
        {
            public int value = 1;
            public void PrintValue()
            {
                Debug.Log("A.value=" + value);
            }
        }
        class B : A
        {
            public new int value = 2;
        }
        void Start()
        {
            A a = new B();
            B b = (B)a;
            Debug.Log(a.value); // 1
            Debug.Log(b.value); // 2
            a.PrintValue();     // A.value=1
            b.PrintValue();     // A.value=1
        }
    •  案例2:子類隱藏父類的PrintValue
      • class A
        {
            public int value = 1;
            public void PrintValue()
            {
                Debug.Log("A.value=" + value);
            }
        }
        class B : A
        {
            public new int value = 2;
            // 隱藏父類函數
            public new void PrintValue()
            {
                Debug.Log("B.value=" + value);
            }
        }
        void Start()
        {
            A a = new B();
            B b = (B)a;
            a.PrintValue();     // A.value=1
            b.PrintValue();     // B.value=2
        }
    •  案例3:子類重寫父類的PrintValue
      • class A
        {
            public int value = 1;
            public virtual void PrintValue()
            {
                Debug.Log("A.value=" + value);
            }
        }
        class B : A
        {
            public new int value = 2;
            // 重寫父類函數
            public override void PrintValue()
            {
                Debug.Log("B.value=" + value);
            }
        }
        void Start()
        {
            A a = new B();
            B b = (B)a;
            a.PrintValue();     // B.value=2
            b.PrintValue();     // B.value=2

 

  • struct 、class

    • 類 是 引用類型,結構體 是 值類型

    • 因此結構體不能=null,也不需要使用new

    • 結構體 不支持繼承

    • 結構體 不能聲明 默認(無參)構造函數,不能聲明 析構函數

    • 結構體成員 不能指定為 abstract、virtual 或 protected

 

  • 抽象函數(abstract) 、虛函數(virtual)

    • abstract:父類只聲明沒有具體實現,子類必須重寫實現父類中的抽象函數

    • virtual:子類可以不重寫父類的虛函數

 

  • 抽象類(abstract) 、接口(interface)

    • 抽象類:依然是一個類,不能被實例化,它仍然包含類的函數

    • 接口:相當於是個規則,里面只能有方法、屬性、索引、事件

    • 抽象類:有抽象的方法,也有不抽象的方法。子類必須實現父類的抽象方法

    • 接口:繼承了接口后,所有的接口方法都必須實現

    • 一個類只能繼承一個父類,但是可以實現多個接口

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM