這篇筆記會接續上一篇的內容,繼續講解類別相關的觀念與功能。
附註,避免文章過於冗長,少數範例程式碼有些並不是完整貼上,敬請見諒!
有些會根據 上一篇 的遊戲情境式範例做擷取或修改,歡迎參考。
【C#學習筆記】07《類別(Class)》
【C#學習筆記】09《類別(Class)》函式多載、靜態成員
在類別之中,成員與成員之間可以互相引用,不需要透過類別名稱.成員進行呼叫。
public void Move() // 玩家移動
{
Console.WriteLine($"{Name} is moving.");
}
public void AttackEnemy() // 玩家攻擊
{
Move();//直接呼叫
Console.WriteLine($"{Name} attacks enemy!");
}
繼承允許一個新類別(子類別)自動擁有另一個現有類別(父類別)的屬性和方法,目的在於減少重複程式碼,建立清晰的資料階層關係。用「動物」與「特定動物」的關係來解釋,父類別(基底類別Base Class):動物(Animal)及子類別(衍生類別 Derived Class):狗(Dog),動物具有年齡、體重、叫聲、吃東西等屬性及行為,而狗直接繼承這些特徵,不需要重新編寫「年齡」或「呼吸」的程式碼,並能進一步擴充自己專屬的「品種」與「汪汪叫」。
父類別(基底類別Base Class):動物(Animal)
namespace Class_02;
public class Animal
{
public string Name;
public int Age { get; set; } = 0;
public void Eat()
{
Console.WriteLine($"{Name} is eating");
}
public void MakeSound()
{
Console.WriteLine($"{Name} makes a sound");
}
}
子類別(衍生類別Derived Class):狗(Dog)
namespace Class_02;
public class Dog : Animal
{
public Dog()
{
Name = "Dog";
}
public Dog(string name, int age)
{
Name = name;
Age = age;
}
}
using Class_02;
var dog1 = new Dog("Kevin", 3);
dog1.MakeSound("Bark Bark Bark");
在C#裡,virtual和override是用來做「多型(Polymorphism)」的核心機制,讓子類別可以改寫父類別的行為。virtual:父類別先定義「這個功能之後可以被改寫」。override:子類別實際把它改掉
沒有繼承的情況下 ,當父類別與子類別都共同擁有MakeSound這個方法成員
父類別
namespace Class_02;
public class Animal
{
public string Name;
public int Age { get; set; } = 0;
public void Eat()
{
Console.WriteLine($"{Name} is eating");
}
public void MakeSound()
{
Console.WriteLine($"null");
}
}
子類別
namespace Class_02;
public class Dog : Animal
{
public Dog()
{
Name = "Dog";
}
public Dog(string name, int age)
{
Name = name;
Age = age;
}
public void MakeSound()
{
Console.WriteLine("Bark Bark Bark");
}
}
namespace Class_02;
public class Cat : Animal
{
public Cat()
{
Name = "Dog";
}
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public void MakeSound()
{
Console.WriteLine("Meow Meow Meow");
}
}
使用foreach依序呼叫
using Class_02;
var beast = new Animal();
var dog1 = new Dog("Kevin", 3);
var cat1 = new Cat("Mittens", 2);
dog1.MakeSound();// Output: Bark Bark Bark
cat1.MakeSound();// Output: Meow Meow Meow
Animal[] animals = { beast, dog1, cat1 };
foreach (var animal in animals)
{
animal.MakeSound();
}
結果顯示,獨立使用.MakeSound跟foreach呼叫的.MakeSound結果不同。foreach會顯示null,這是因為所有子類別都是繼承父類別的MakeSound。
加上virtual及override後的完整程式碼:
namespace Class_02;
public class Animal
{
public string Name;
public int Age { get; set; } = 0;
public void Eat()
{
Console.WriteLine($"{Name} is eating");
}
public virtual void MakeSound()
{
Console.WriteLine($"null");
}
}
namespace Class_02;
public class Dog : Animal
{
public Dog()
{
Name = "Dog";
}
public Dog(string name, int age)
{
Name = name;
Age = age;
}
public override void MakeSound()
{
Console.WriteLine("Bark Bark Bark");
}
}
namespace Class_02;
public class Cat : Animal
{
public Cat()
{
Name = "Dog";
}
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public override void MakeSound()
{
Console.WriteLine("Meow Meow Meow");
}
}
using Class_02;
var beast = new Animal();
var dog1 = new Dog("Kevin", 3);
var cat1 = new Cat("Mittens", 2);
dog1.MakeSound();// Output: Bark Bark Bark
cat1.MakeSound();// Output: Meow Meow Meow
Animal[] animals = { beast, dog1, cat1 };
foreach (var animal in animals)
{
animal.MakeSound();
}
private、public、protected是C#的「存取修飾詞(Access Modifier)」
public = 對外公開,任何地方都能存取。
private = 只有自己能用,通常用於不希望外部亂改資料。
protected = 外部不能用,但子類別可以用,是「繼承」很重要的修飾詞。
根據上述範例,我們將父類別的MakeSound方法成員改成protected,我們會發現以下這行會報錯。
這是因為參數animal並不存在於父子類別之中,因此無法使用。
foreach (var animal in animals)
{
animal.MakeSound();//error
}
以上三種,基本上就是常用的存取修飾詞,另外還有欄位修飾詞(Field Modifier)將會在後續章節提及!
繼承(Inheritance)的使用,會提高整體專案的關聯性及可閱讀性,為後續的開發及維護提供清晰的程式碼結構。然而,這也可能讓程式碼間的耦合度(Tight Coupling)大幅提高,使得父類別與子類別之間產生強烈的依賴關係。當父類別的底層邏輯發生變更時,往往會引發「牽一髮而動全身」的連帶錯誤,進而破壞系統的封裝性並增加重構的難度,須謹慎設計架構,才能在結構清晰與架構彈性之間取得最佳平衡。