多型 是物件導向三大支柱之一(封裝、繼承、多型),意指「同一介面,多種實作」。C# 支援兩種多型:靜態多型(編譯期決定)與動態多型(執行期決定)。掌握兩者差異與適用場景,是寫出彈性、可擴充程式碼的關鍵。
靜態多型在編譯階段就決定呼叫哪個方法,又稱早期綁定。C# 提供兩種機制:
public class Printer {
public void Print(int value) => Console.WriteLine($"整數: {value}");
public void Print(double value) => Console.WriteLine($"浮點數: {value:F2}");
public void Print(string value) => Console.WriteLine($"字串: {value}");
public void Print(int value, string prefix) => Console.WriteLine($"{prefix}: {value}");
}
// 使用
var p = new Printer();
p.Print(42); // 呼叫 Print(int)
p.Print(3.14159); // 呼叫 Print(double)
p.Print("Hello"); // 呼叫 Print(string)
p.Print(100, "ID"); // 呼叫 Print(int, string)
規則:多載方法必須在參數型別、數量、順序上有差異;僅傳回型別不同無法構成多載。
動態多型透過繼承階層與虛擬方法實現,在執行階段根據物件實際型別決定呼叫哪個方法(晚期綁定)。核心機制有二:
抽象類別(abstract class)無法實例化,僅供繼承。抽象方法只有宣告、無實作,衍生類別必須實作(除非衍生類別也是抽象類別)。
public abstract class Shape {
public abstract double Area(); // 抽象方法:強制子類實作
public void Display() => Console.WriteLine($"面積: {Area()}"); // 具體方法:可直接繼承
}
public class Rectangle : Shape {
public double Width { get; set; }
public double Height { get; set; }
public override double Area() => Width * Height; // 必須 override
}
public class Circle : Shape {
public double Radius { get; set; }
public override double Area() => Math.PI * Radius * Radius;
}
虛擬方法(virtual)提供預設實作,衍生類別可選擇覆寫(override):
public class Animal {
public virtual void MakeSound() => Console.WriteLine("動物發出聲音");
}
public class Dog : Animal {
public override void MakeSound() => Console.WriteLine("汪汪!");
}
public class Cat : Animal {
public override void MakeSound() => Console.WriteLine("喵~");
}
// 多型呼叫
Animal[] animals = { new Dog(), new Cat(), new Animal() };
foreach (var a in animals) a.MakeSound();
// 輸出:汪汪! 喵~ 動物發出聲音
| 關鍵字 | 用途 | 是否強制實作 | 可否再被覆寫 |
|---|---|---|---|
virtual | 基類提供預設實作 | 否 | 是 |
override | 衍生類覆寫虛擬/抽象方法 | 視基類而定 | 預設可,除非加 sealed |
abstract | 抽象類別定義契約,無實作 | 是(非抽象衍生類) | 是 |
sealed override | 封鎖進一步覆寫 | — | 否 |
public abstract class Shape {
public abstract double Area();
public virtual void Draw() => Console.WriteLine("繪製圖形...");
}
public class Rectangle : Shape {
public double W { get; set; }, H { get; set; }
public override double Area() => W * H;
public override void Draw() => Console.WriteLine($"繪製矩形 ({W}x{H})");
}
public class Triangle : Shape {
public double Base { get; set; }, Height { get; set; }
public override double Area() => Base * Height / 2;
public override void Draw() => Console.WriteLine($"繪製三角形 (底{Base} 高{Height})");
}
public class ShapeProcessor {
public void ProcessAll(IEnumerable shapes) {
foreach (var s in shapes) {
s.Draw(); // 動態綁定:呼叫實際型別的 Draw
Console.WriteLine($"面積: {s.Area():F2}");
}
}
}
// 使用
var shapes = new List {
new Rectangle { W = 10, H = 5 },
new Triangle { Base = 6, Height = 4 },
new Rectangle { W = 3, H = 3 }
};
new ShapeProcessor().ProcessAll(shapes);
覆寫方法中可透過 base.Method() 呼叫基底實作:
public override void Draw() {
base.Draw(); // 先執行基底邏輯
Console.WriteLine(" -> 矩形專屬繪製");
}
C# 8.0+ 介面支援 default 實作,提供另一種多型形式(類似 Traits):
public interface ILogger {
void Log(string msg);
void LogError(string msg) => Log($"[ERR] {msg}"); // 預設實作
}
C# 7.0+ switch 運算式 / is 模式比對提供更優雅的型別分派:
public static string Describe(Shape s) => s switch {
Rectangle r => $"矩形 {r.W}x{r.H}",
Triangle t => $"三角形 底{t.Base} 高{t.Height}",
_ => "未知圖形"
};
virtual。sealed override 防止繼承鏈過深導致脆弱性。A: 需共享程式碼/狀態 → 抽象類別;定義跨層級能力契約 → 介面。C# 單一繼承限制下,介面更靈活。
A: 靜態多型(多載/泛型)編譯期決定,零執行期成本;動態多型需虛擬表查找,極微小開銷,通常可忽略。
A: 結構體不支援繼承,但可實作介面,透過介面型別實現多型(會發生裝箱)。