1. 程式人生 > 其它 >構造方法可以重寫嗎_C#學習筆記09--構造方法/析構方法/繼承/多型/封裝

構造方法可以重寫嗎_C#學習筆記09--構造方法/析構方法/繼承/多型/封裝

技術標籤:構造方法可以重寫嗎

一.構造方法

當建立一個類時, 系統會自動建立一個預設的無參構造方法, 這個方法和類同名, 在建立物件的時候自動呼叫. 預設的構造方法裡面什麼也沒有, 重寫之後就可以在建立物件的時候同時搞點事情了.

構造方法主要負責對類裡面的欄位進行初始化。

1.1 格式:

public 類名(形參)
{
    程式碼段;
}

// 栗子
class Person
{
    public string name;
    public int age;
    public string gender;

    public Person(string name, int age, string gender)
    {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}

1.2 注意事項:

  1. 構造方法必須要和類名重名;
  2. 構造方法沒有返回值並且不加void, 但是可以有引數;
  3. 構造方法可以有多個過載;
  4. 構造方法只能在建立物件的時候才可以呼叫, 不能手動呼叫;
  5. 如果有帶引數的構造方法, 想要呼叫預設構造必須顯式的寫出來;

二.析構方法

2.1 使用: 物件銷燬之前都會自動呼叫析構方法

形象的來說, 我們玩遊戲的時候把boss打死之後, boss會爆出各種裝備金幣,藍啊之類的, 這些方法我們就可以在析構方法裡面寫;

2.2 語法:

~類名()
{
    程式碼段;
}

// 栗子
~Person()
{
    Console.WriteLine("我還會回來的......");
}

2.3 注意:

  1. 只有類才有析構方法, 一個類只能有一個析構方法.
  2. 無法繼承和過載析構方法;
  3. 不能手動呼叫, 物件在被銷燬之前會自動呼叫;
  4. 析構方法沒有訪問修飾符, 沒有引數;
  5. 必須和類名同名;

三.繼承

3.1 概念:

繼承用於建立可重用、擴充套件和修改在其他類中定義的行為的新類。 其成員被繼承的類稱為“基類”,繼承這些成員的類稱為“派生類”。 派生類只能有一個直接基類(C#中))。 但是,繼承是可傳遞的。 如果 ClassB 派生出 ClassC,ClassA 派生出 ClassB,則 ClassC 會繼承 ClassB 和 ClassA 中宣告的成員。

3.2 格式:

class 子類類名 : 父類類名 
{
    子類新增加的成員;
}

學過構造方法之後, 就想給每個類都寫個構造方法,以便簡化初始化操作. 但是嘗試過後發現如果父類寫了帶引數的構造方法, 子類就會報錯了, 如何解決? base帶你飛.

3.3 繼承父類的構造方法:

孩子在建立物件的時候, 會先呼叫父類的構造方法, 如果父類是無參的就沒有影響, 但是如果父類有有引數的構造方法, 孩子就需要傳引數. 呼叫父類有參的構造方法有兩個方法:

  1. 在父類中顯式的將預設構造寫出來;
  2. 子類在重寫構造方法的時候利用base關鍵字傳入父類的引數;
// 父類有參的構造方法
        public Zomby(int blood, int attackPower, int speed)
        {
            this.blood = blood;
            this.attackPower = attackPower;
            this.speed = speed;
        }

//子類有參的構造方法
// base後面要將父類有的引數寫上
        public  OstableZomby(int blood, int attackPower, int speed, int defense):base(blood, attackPower, speed)
        {
            this.defense = defense;
        }

3.4 注意:

  1. 繼承具有單向性, a -> b , b不能繼承a;
  2. C#中,只能繼承一個父類但是可以多重繼承;
  3. 父類也叫基類, 子類也叫派生類;
  4. 繼承具有傳遞性; a -> b, b -> c a就具有了c和b所有屬性和方法;
  5. 基類有的,派生類都有;派生類有的,基類可能沒有。即:派生類可以賦值給基類,而基類不能賦值給派生類。

子類在繼承的時候不管想不想都會繼承父類的所有欄位和方法, 那麼如果子類想用同樣的方法搞特殊呢? 那就要引入多型了.

四.多型

栗子: 就拿我們建立的殭屍們來說, 殭屍都會攻擊, 但是普通殭屍(小口咬), 路障殭屍(大口咬)和鐵桶殭屍(超大口咬)的攻擊方法不同, 這就是多型;

4.1重寫父類方法的多型:

1.在訪問修飾後面加上new關鍵字後重寫:
// 父類的Attack方法
        public void Attack()
        {
            Console.WriteLine("殭屍在攻擊");
        }

// 子類重寫Attack方法
// 加不加new都可以
        public new void Attack()
        {
            Console.WriteLine("路障殭屍在攻擊");
        }

那麼問題來了, 我雖然重寫了父類的Attack方法, 但是我在某個地方還是想要使用父類的Attack方法的時候怎麼辦呢?

加上 base關鍵字:
// 子類中
        public void ObjAttack()
        {
            // 加上 base關鍵之之後呼叫的就是父類的攻擊方法
            base.Attack();
        }

//Main函式
            OstableZomby ozb = new OstableZomby(150, 20, 20, 50);
            ozb.Attack();
            ozb.ObjAttack();

92581b03d9e35fc774877f10c8cb4ff2.png
結果

小結base:

  1. 呼叫基類被派生類重寫的方法;
  2. 指定呼叫基類中某一個指定的構造方法;

4.2 virtual override 的多型

用這種方法來實現多型需要做兩件事情:

1> 將父類中要實現多型性的方法改成虛方法, 即在返回值型別前加關鍵字 virtual:
// 殭屍父類
// 宣告virtual方法的目的是允許子類重寫父類方法實現自己的特性
        public virtual void Attack()
        {
            Console.WriteLine("殭屍在攻擊");
        }
2>子類重寫父類的方法, 即將方法隱藏的關鍵字 new 改為 override.
// 路障殭屍子類
// 方法重寫一定要配合virtual方法一起使用, 也就是宣告override的方法, 子類中一定要存在一個同樣的virtual方法
        public override void Attack()
        {
            Console.WriteLine("路障殭屍在攻擊");
        }

// 鐵通殭屍子類
        public override void Attack()
        {
            Console.WriteLine("鐵桶殭屍在攻擊");
        }

84174533ad244ac4d07af8d0907306d1.png
多型的執行結果

假如說現在有20個路障殭屍和10個鐵桶殭屍, 他們現在都要進行攻擊怎麼實現? 藉助陣列唄. 那麼問題又來了, 陣列型別是什麼? 陣列中既有路障殭屍類又有鐵桶殭屍類, 那想著能不能定義成他們的父類普通殭屍類呢? 要解決這個問題就需要用到里氏轉換了.

4.3里氏轉換

里氏轉換的本質其實就是將父類強制轉換成子類.

4.3.1 里氏轉換第一原則

子類物件可以直接賦值給父類物件
// Main函式中
            Zomby zb = new OstableZomby();
// 將子類物件賦值給父類物件, 此時會存在隱式轉換
// 隱式轉換會導致部分資料丟失, 此時Zomby 只能呼叫 Zomby類中的成員了;

4.3.2 里氏轉換第二原則

父類可以通過強制轉換的方式轉換成子類, 此時就可以呼叫子類中的成員了
  1. 使用is關鍵字判斷型別
  2. 使用 as 關鍵字進行強制轉換
// Main函式中
            // 判斷物件是不是 ObstableZomny 型別
            if(zb is OstableZomby)
            {
                OstableZomby ozb = zb as OstableZomby;
            }

使用 as 關鍵字進行強轉即使不成功也不會報錯, 而是返回 null, 但是強行的 "ozb = (OstableZomby)zb" 轉換如果不成功的話, 程式就會報錯終止.

那麼現在我們就是將陣列問題解決了:

// Main函式中
// 先定義30個物件, 我就簡單寫了
            Zmoby zb = new Zomby();
            OstableZomby ozb = new OstableZomby();
            MetalZomby mzb = new MetalZomby();
            OstableZomby ozb1 = new OstableZomby();
            MetalZomby mzb1 = new MetalZomby();
            OstableZomby ozb2 = new OstableZomby();
            MetalZomby mzb2 = new MetalZomby();
            OstableZomby ozb3 = new OstableZomby();
            MetalZomby mzb3= new MetalZomby();
            OstableZomby ozb4 = new OstableZomby();
            MetalZomby mzb4 = new MetalZomby();
// 定義父類型別的陣列並初始化
            Zmoby[] zbs = { ozb, mzb, ozb1, mzb1, ozb2, mzb2, ozb3, mzb3, ozb4, mzb4 };
// 里氏轉化並列印
            foreach (Zmoby item in zbs)
            {
                if (item is OstableZomby)
                {
                    OstableZomby o = item as OstableZomby;
                    o.Attack();
                }
                if (item is MetalZomby)
                {
                    MetalZomby o = item as MetalZomby;
                    o.Attack();
                }
            }

98b7626a6c3eba19e2c4f2ae458ce24a.png

五.封裝

在C#中, 可以把類裡面的這些欄位進行私有化保護, 併為其新增共有屬性, 在類的外面不能通過物件直接訪問這些欄位, 想要訪問只能通過訪問屬性的方式來完成, 這就是封裝. 具體操作在記錄屬性的時候其實就已經寫了, 這裡就不再記錄了.

補充.結構體和類的區別:

  1. 結構體是值型別, 而類是引用型別;
  2. 結構體只能有兩個構造方法, 一個是無參預設的(且不能顯式寫出來!), 一個是將所有欄位都初始化的;
  3. 結構體不能被繼承, 而類可以被繼承;
  4. 類有多型性, 結構體沒有多型性;
  5. 一般只操作一些資料或是以資料為主的用結構型別, 當表示有一些層級關係的時候用類;
  6. 結構體有裝箱拆箱的操作(值型別), 而類沒有裝箱拆箱的操作(引用型別);
int a = 20;
object obj = (oject)a; // 裝箱操作  值型別轉換為引用型別
int b = (int)obj; // 拆箱操作  引用型別轉換為值型別

=======================================================

思維導圖

409a1ad1740ed73b565432ef8870b678.png