C#繼承

      在〈C#繼承〉中尚無留言

為何要繼承

底下程式碼中, 宣告了一個Pikachu的類別, 裏面有等級(level), 身高(h), 体重(w) 三個物件變數. 試想, 如果你是任天堂這間公司裏的程式設計師, 老闆叫你要創造出一千多種的神奇寶貝, 而每種神奇寶貝都同樣會有這三個物件變數. 那麼每創造一種神奇寶貝後, 這三個變數不就都要重複寫一次嗎。

 class Pikachu
 {
     int leve;
     double w, h;
 }
 class Eve
 {
     int leve;
     double w, h;
 }

所以是不是有什麼方法, 可以不用重複寫個上千次呢? 此時繼承就可以解決這個問題了. 如下程式碼

 class Pokemon
 {
     private int leve;
     public double w, h;
 }
 class Pikachu : Pokemon
 {
 }
 class Eve:Pokemon
 {
 }

上述的Pokemon宣告了所有類別共通的變數, 然後使用 “:” 繼承給Pikachu 及 Eve, 而在Pikachu及Eve類別中什麼都不用要寫, 就會有level, w, h三個變數了。

繼承後的權限

子類別繼承後, 父類別的public屬性及方法全都會過繼到子類別, 因此在 Main(0中可以直接使用 p1.w.

而private變數呢, 一般人是說沒有繼承給子類別. 但這種說法好像是錯的. 實際上好像是有繼承給子類別,  只是在子類別中無法使用.  這就好比父親給你一個金庫, 但他卻忘了把鑰匙留給你, 所以這個金庫就無法打開.

因為父類別若有private變數, 而此變數需要在子類別中存取的話, 那在父類別就一定要把鑰匙交待下去, 也就是在父類別中要有 public 方法來進行存取.

static void Main(string[] args)
{
    Pikachu p1=new Pikachu();
    p1.w = 10.3f;
    p1.level = 10; //Error
}

物件變數覆蓋

為什麼上面說private變數好像有繼承給子類別呢. 假設在子類別有一個變數, 其名稱與父類別的變數名稱一模一樣, 那子類別的變數就會蓋掉父類別的變數.

在下面的代碼中, p.level=100, 則是使用子類別自己的變數,
而p.setLevel(5), 卻是改到了父類別原先的level. 因為推估, private 變數一樣會下降到子類別中, 在子類別保存了一份來自父類別的資訊.

    
    static void Main(string[] args)
    {
            Pikachu p = new Pikachu();
            p.setLevel(6);
            p.level = 100;
            Console.WriteLine("p.getLevel()={0}",p.getLevel(), p.level);
            Console.WriteLine("p.level={0}", p.level);
    }

    class Pokemon
    {
        private int level;//物件變數
        public double w, h;//物件變數
        public static int count;//類別變數
        public void setLevel()
        {
            this.level = 1;
        }
        public void setLevel(int level)
        {
            if (level > 10) this.level = level;
            else this.level = level ;
        }
        public int getLevel()
        {
            return level;
        }
    }
    class Pikachu : Pokemon
    {
        public int level;
    }

方法覆蓋

當子類別的方法與父類別的方法同樣名稱時, 此子類別的方法會覆蓋掉父類別的方法, 然後父類別方法會屍骨無存.

在下面代碼中, 父類別的 level為 private, 所以只有父類別的 setLevel()方法才可以存取. 但子類別又把父親留下來的鑰題給蓋了上去, 且在子類別又無法存取 level 變數, 那不就死定了嗎.

此時只好委託父類別的方法設定. 但父類別不是屍骨無存了嗎. 那只好採用觀落音法 base.setLevel();

   class Pokemon
    {
        private int level;//物件變數
        public double w, h;//物件變數
        public static int count;//類別變數
        public void setLevel(int level)
        {
            if (level > 10) this.level = level;
            else this.level = level ;
        }
        public int getLevel()
        {
            return level;
        }
    }
    class Pikachu : Pokemon
    {
        public void setLevel(int level)
        {
            base.setLevel(level * 10);
        }
    }

建構子覆蓋

建構子其實沒有覆蓋的問題, 因為子類別的建構子名稱一定跟父類別不一樣.

但請看如下代碼, 在Main()中new 出一隻Pikachu()後,會印出Pokemon出生, 然後才會印出皮卡丘出來.

生出皮卡丘跟Pokemon有啥關係呢?, 原來編譯器會在Pikachu()建構子後面自動加上 :base(), 如下
public Pikachu():base(), 所以會先執行父類別建構子, 再執行子類別建構子

那麼, Pokemon類別因為沒有父類別, 那他的建構子會不會也自動加base()呢? 答案是~~會的. 那加上之後, 是要執行什麼東西啊?

原來 class Pokemon雖沒有繼承任何父類別, 那麼編譯器就會自動加為如下
class Pokemon : Object

也就是說, 所有類別,  都是繼承Object而來的. Object是所有類別的父類別, 只有Object沒有父類別

    class Pokemon
    {
        public Pokemon()
        {
            Console.WriteLine("Pokemon出生");
        }
    }
    class Pikachu : Pokemon
    {
        public Pikachu()
        {
            Console.WriteLine("皮卡丘出生");
        }
    }

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *