首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 繼承完整指南:基底類別、建構子鏈、多重繼承替代方案與最佳實踐

     編輯:C# 入門

C# 繼承完整指南:基底類別、建構子鏈、多重繼承替代方案與最佳實踐

繼承 是物件導向程式設計的核心機制,讓新類別(衍生類別)取得既有類別(基底類別)的成員,實現程式碼重用與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✓同組件✓✗

建構子鏈:base() 呼叫機制

衍生類別建構子必須呼叫基底類別建構子(隱式或顯式),確保基底部分正確初始化:

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# 不支援多重繼承:替代方案

C# 類別不能繼承多個基底類別,但可透過以下方式達成類似效果:

1. 介面組合

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("鴨子游泳");
}

2. 組合優於繼承

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(); }
}

3. 預設介面方法

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;
    }
}

最佳實踐

  • 組合優於繼承:預設選擇組合,只有明確的 IS-A 關係才用繼承。
  • 基底類別設計為抽象:避免實例化不完整的基底類別,強制衍生類別實作契約。
  • 慎用 protected 欄位:優先用 protected 屬性/方法封裝,保留封裝性。
  • 建構子鏈清晰:衍生類別建構子明確呼叫 base(...),傳遞必要參數。
  • 避免深層繼承樹:超過 3-4 層難以維護,考慮扁平化或組合。
  • 文件化契約:XML 註解說明虛擬方法的預期行為、覆寫約束。

常見問題 FAQ

Q: 衍生類別能存取基底類別的 private 成員嗎?

A: 不能。但可透過基底類別的 protected 或 public 成員間接存取。

Q: 什麼時候用 abstract class,什麼時候用 interface?

A: 參考 介面 vs 抽象類別 章節。簡單說:共享實作用 abstract class,定義能力用 interface。

Q: 多重繼承真的有必要嗎?

A: 現代設計傾向組合/介面,多重繼承常導致鑽石問題與複雜性。C# 刻意不支援,引導更好的設計。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved