繼承 是物件導向程式設計的核心機制,讓新類別(衍生類別)取得既有類別(基底類別)的成員,實現程式碼重用與IS-A(是一種)關係建模。C# 採用單一繼承模型:一個類別只能直接繼承一個基底類別,但可實作多個介面。
public class Animal { // 基底類別
public string Name { get; set; }
protected int Age { get; set; } // 衍生類別可存取
private string Secret { get; set; } // 僅基底類別可存取
public Animal(string name, int age) {
Name = name; Age = age; Secret = "hidden";
}
public virtual void Speak() => Console.WriteLine($"{Name} 發出聲音");
}
public class Dog : Animal { // 衍生類別
public string Breed { get; set; }
public Dog(string name, int age, string breed) : base(name, age) {
Breed = breed;
}
public override void Speak() => Console.WriteLine($"{Name} 汪汪叫"); // 覆寫
}
存取層級對繼承的影響:
| 修飾詞 | 基底類別 | 衍生類別 | 外部程式碼 |
|---|---|---|---|
public | ✓ | ✓ | ✓ |
protected | ✓ | ✓ | ✗ |
internal | ✓ | 同組件✓ | 同組件✓ |
protected internal | ✓ | ✓ | 同組件✓ |
private | ✓ | ✗ | ✗ |
private protected | ✓ | 同組件✓ | ✗ |
衍生類別建構子必須呼叫基底類別建構子(隱式或顯式),確保基底部分正確初始化:
public class Shape {
public string Color { get; }
public Shape(string color) => Color = color; // 無預設建構子
}
public class Rectangle : Shape {
public double Width { get; }
public double Height { get; }
// 必須顯式呼叫 base(color),否則編譯錯誤
public Rectangle(string color, double w, double h) : base(color) {
Width = w; Height = h;
}
}
執行順序:基底類別靜態建構子 → 衍生類別靜態建構子 → 基底類別實例建構子 → 衍生類別實例建構子。
基底類別標記 virtual,衍生類別用 override 提供新實作,執行期多型生效:
public class Base { public virtual void M() => Console.WriteLine("Base"); }
public class Derived : Base { public override void M() => Console.WriteLine("Derived"); }
Base b = new Derived(); b.M(); // 輸出 "Derived" (動態綁定)
衍生類別用 new 定義同名成員,不發生多型,僅隱藏基底成員:
public class Base { public void M() => Console.WriteLine("Base"); }
public class Derived : Base { public new void M() => Console.WriteLine("Derived"); }
Base b = new Derived(); b.M(); // 輸出 "Base" (靜態綁定)
Derived d = new Derived(); d.M(); // 輸出 "Derived"
建議:優先使用 override;只有必須改變簽章或刻意阻斷多型時才用 new(會產生編譯器警告)。
sealed class:禁止被繼承(如 string、struct 隱含密封)。sealed override:允許繼承類別,但禁止進一步覆寫該方法。public sealed class Singleton { /* 無法繼承 */ }
public class Base { public virtual void M() {} }
public class Middle : Base { public sealed override void M() {} }
public class Leaf : Middle { /* 無法 override M */ }
C# 類別不能繼承多個基底類別,但可透過以下方式達成類似效果:
public interface IFlyable { void Fly(); }
public interface ISwimmable { void Swim(); }
public class Duck : IFlyable, ISwimmable {
public void Fly() => Console.WriteLine("鴨子飛行");
public void Swim() => Console.WriteLine("鴨子游泳");
}
public class Engine { public void Start() => Console.WriteLine("引擎啟動"); }
public class GPS { public void Navigate() => Console.WriteLine("導航中"); }
public class Car {
private readonly Engine _engine = new Engine();
private readonly GPS _gps = new GPS();
public void Drive() { _engine.Start(); _gps.Navigate(); }
}
C# 8.0+ 介面可提供預設實作,減少重複程式碼:
public interface ILoggable {
void Log(string msg) => Console.WriteLine($"[LOG] {msg}"); // 預設實作
}
public class Service : ILoggable { /* 直接擁有 Log 方法 */ }
public abstract class Entity {
public Guid Id { get; } = Guid.NewGuid();
public DateTime CreatedAt { get; } = DateTime.UtcNow;
protected Entity() {} // 受保護建構子,防止外部直接實例化
}
public class User : Entity {
public string Username { get; set; }
public string Email { get; set; }
public User(string username, string email) {
Username = username; Email = email;
}
}
public class AdminUser : User {
public string[] Permissions { get; set; }
public AdminUser(string username, string email, string[] permissions)
: base(username, email) { // 呼叫 User 建構子
Permissions = permissions;
}
}
protected 屬性/方法封裝,保留封裝性。base(...),傳遞必要參數。A: 不能。但可透過基底類別的 protected 或 public 成員間接存取。
A: 參考 介面 vs 抽象類別 章節。簡單說:共享實作用 abstract class,定義能力用 interface。
A: 現代設計傾向組合/介面,多重繼承常導致鑽石問題與複雜性。C# 刻意不支援,引導更好的設計。