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

C# 多型深度解析:靜態與動態多型、虛擬方法、抽象類別實戰

     編輯:C# 入門

C# 多型深度解析:靜態與動態多型、虛擬方法、抽象類別實戰

多型 是物件導向三大支柱之一(封裝、繼承、多型),意指「同一介面,多種實作」。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)

規則:多載方法必須在參數型別、數量、順序上有差異;僅傳回型別不同無法構成多載。

動態多型:執行期綁定

動態多型透過繼承階層與虛擬方法實現,在執行階段根據物件實際型別決定呼叫哪個方法(晚期綁定)。核心機制有二:

  • 抽象類別 + 抽象方法
  • 虛擬方法 + override 覆寫

抽象類別與抽象方法

抽象類別(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 / abstract / sealed

關鍵字用途是否強制實作可否再被覆寫
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 防止繼承鏈過深導致脆弱性。
  • 里氏替換原則 (LSP):衍生類別必須能完全替換基底類別而不破壞正確性。
  • 避免在建構子呼叫虛擬方法:此時衍生類別尚未初始化,可能導致不可預期行為。

常見問題 FAQ

Q: 什麼時候用抽象類別,什麼時候用介面?

A: 需共享程式碼/狀態 → 抽象類別;定義跨層級能力契約 → 介面。C# 單一繼承限制下,介面更靈活。

Q: 靜態多型與動態多型效能差異?

A: 靜態多型(多載/泛型)編譯期決定,零執行期成本;動態多型需虛擬表查找,極微小開銷,通常可忽略。

Q: 結構體支援多型嗎?

A: 結構體不支援繼承,但可實作介面,透過介面型別實現多型(會發生裝箱)。

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