首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 面向對象三大支柱:封裝、繼承、多態深度解析與實戰範例

     編輯:關於C語言

面向對象程式設計 (OOP) 的靈魂在於封裝、繼承、多態三大特性。它們不只是語法糖,更是管理軟體複雜度、實現高內聚低耦合的核心工具。本文結合 C# 語言特性,從原理、語法、設計意圖到完整實戰範例,帶你透徹掌握 OOP 精髓。

一、封裝:邊界的藝術

核心定義

「將資料與操作資料的方法綁定在一起,對外僅暴露必要介面,隱藏內部實現細節。」

C# 存取修飾詞體系

修飾詞可見範圍典型用途
private僅本類別內部欄位、輔助方法
protected本類別 + 衍生類別供子類擴充的核心邏輯
internal同一組件組件內部服務協定
protected internal同組件 或 衍生類別跨組件繼承場景
public任意處對外公共 API
private protected同組件且衍生類別極度受限的擴充點

屬性封裝模式

public class BankAccount
{
    private decimal _balance; // 隱藏實現細節

    public decimal Balance 
    { 
        get => _balance; 
        private set // 外部不可直接修改
        {
            if (value < 0) throw new ArgumentException("餘額不可為負");
            _balance = value;
        }
    }

    public void Deposit(decimal amount)
    {
        if (amount <= 0) throw new ArgumentException("存款金額需為正");
        Balance += amount; // 內部可用 private set
    }

    public bool Withdraw(decimal amount)
    {
        if (amount > Balance) return false;
        Balance -= amount;
        return true;
    }
}

封裝三大收益

  1. 安全性:不變條件由類別內部守護,外部無法輸入非法狀態
  2. 可維護性:內部演算法變更 (如改用資料庫、快取) 不影響呼叫端
  3. 降低耦合:呼叫端依賴抽象介面而非具體實現

二、繼承:擴充與分類

語法與語意

public abstract class Shape // 抽象基類:定義契約、共享邏輯
{
    public string Name { get; protected set; }
    protected Shape(string name) => Name = name;

    // 模板方法:定義演算法骨架,延遲具體步驟給子類
    public void PrintInfo()
    {
        Console.WriteLine($"{Name}: Area={CalculateArea():F2}, Perimeter={CalculatePerimeter():F2}");
    }

    public abstract double CalculateArea();      // 強制子類實現
    public abstract double CalculatePerimeter(); // 強制子類實現

    // 可選:提供預設實現供子類複寫
    public virtual void Draw() => Console.WriteLine($"Drawing {Name}...");
}

衍生類別實作

public class Circle : Shape
{
    public double Radius { get; set; }
    public Circle(double radius) : base("Circle") => Radius = radius;

    public override double CalculateArea() => Math.PI * Radius * Radius;
    public override double CalculatePerimeter() => 2 * Math.PI * Radius;
    
    public override void Draw() => Console.WriteLine($"🔴 繪製半徑 {Radius} 的圓");
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public Rectangle(double w, double h) : base("Rectangle") { Width = w; Height = h; }

    public override double CalculateArea() => Width * Height;
    public override double CalculatePerimeter() => 2 * (Width + Height);
}

繼承 vs 組合:設計決策表

場景選擇理由
「是一種」關係明確 (Circle 是 Shape)繼承語意符合 Liskov 替換原則
需要複用部分行為但非完整契約組合避免破壞封裝、減少耦合
基類頻繁變更組合脆弱基類問題
多重行為組合 (可飛、可游、可叫)介面 + 組合C# 不支援多重繼承

三、多態:同一介面、多種形態

兩種多態形式

  1. 繼承多態 (Subtyping):子類覆寫虛擬方法,透過基類參考呼叫衍生實現
  2. 介面多態:實作同一介面的不同類別,透過介面參考互換使用

經典範例:形狀集合統一處理

var shapes = new List<Shape>
{
    new Circle(10),
    new Rectangle(5, 8),
    new Circle(3.5)
};

foreach (var shape in shapes)
{
    // 編譯期綁定 Shape.PrintInfo,執行期動態分派至具體 CalculateArea/Perimeter
    shape.PrintInfo(); 
    
    // 輸出:
    // Circle: Area=314.16, Perimeter=62.83
    // Rectangle: Area=40.00, Perimeter=26.00
    // Circle: Area=38.48, Perimeter=21.99
}

多態的三大前提

  1. 繼承/實作關係:衍生自共同基類或介面
  2. 方法覆寫:virtual/abstract + override (或介面隱含虛擬)
  3. 基類/介面參考:變數宣告型別為基類或介面

四、綜合實戰:可擴充的圖形編輯器核心


// 1. 介面定義契約 (比抽象類別更鬆耦合)
public interface IShape
{
    string Name { get; }
    double Area { get; }
    double Perimeter { get; }
    void Render(ICanvas canvas);
    bool Contains(Point p); // 擊測
}

// 2. 抽象基類提供共享實現 (模板方法、公用邏輯)
public abstract class ShapeBase : IShape
{
    public abstract string Name { get; }
    public abstract double Area { get; }
    public abstract double Perimeter { get; }

    public virtual void Render(ICanvas canvas) 
        => canvas.DrawPath(GetPath()); // 統一委派給 GetPath

    public abstract GraphicsPath GetPath(); // 子類提供幾何路徑

    public virtual bool Contains(Point p) => GetPath().IsVisible(p);
}

// 3. 具體形狀
public class EllipseShape : ShapeBase
{
    public RectangleF Bounds { get; set; }
    public override string Name => "Ellipse";
    public override double Area => Math.PI * Bounds.Width/2 * Bounds.Height/2;
    public override double Perimeter => Math.PI * (3*(Bounds.Width/2 + Bounds.Height/2) - Math.Sqrt((3*Bounds.Width/2 + Bounds.Height/2)*(Bounds.Width/2 + 3*Bounds.Height/2)));

    public override GraphicsPath GetPath()
    {
        var path = new GraphicsPath();
        path.AddEllipse(Bounds);
        return path;
    }
}

// 4. 擴充新形狀完全不動既有代碼 (開放封閉原則)
public class TriangleShape : ShapeBase
{
    public PointF P1, P2, P3;
    public override string Name => "Triangle";
    public override double Area => Math.Abs((P1.X*(P2.Y-P3.Y) + P2.X*(P3.Y-P1.Y) + P3.X*(P1.Y-P2.Y))/2);
    public override double Perimeter => Distance(P1,P2)+Distance(P2,P3)+Distance(P3,P1);

    public override GraphicsPath GetPath()
    {
        var path = new GraphicsPath();
        path.AddPolygon(new[]{P1,P2,P3});
        return path;
    }
    private double Distance(PointF a, PointF b) => Math.Sqrt((a.X-b.X)*(a.X-b.X)+(a.Y-b.Y)*(a.Y-b.Y));
}

// 5. 畫布介面 (依賴倒置)
public interface ICanvas
{
    void DrawPath(GraphicsPath path, Pen pen = null, Brush brush = null);
}

// 6. 使用端:完全面向介面程式設計
public class DrawingEditor
{
    private readonly List<IShape> _shapes = new();
    private readonly ICanvas _canvas;

    public DrawingEditor(ICanvas canvas) => _canvas = canvas;

    public void AddShape(IShape shape) => _shapes.Add(shape);
    
    public void RenderAll()
    {
        foreach (var shape in _shapes)
            shape.Render(_canvas); // 多態分派
    }

    public IShape HitTest(Point p) => _shapes.LastOrDefault(s => s.Contains(p));
}

五、SOLID 原則與三大特性的映射關係

SOLID對應特性實踐要點
S 單一職責封裝類別只負責一個業務邏輯,欄位/方法高內聚
O 開放封閉繼承 + 多態擴充靠衍生新類,修改靠抽象介面,既有代碼不動
L 里氏替換繼承 + 多態子類必須能無縫替換基類,前置條件不強化、後置條件不弱化
I 介面隔離多態 (介面)介面細粒度、專一,避免胖介面強迫實作不需要的方法
D 依賴倒置多態高層模組依賴抽象 (介面/抽象類),不依賴具體實現

六、總結

  • 封裝劃定邊界,守護不變量 → private + property + 驗證邏輯
  • 繼承建立分類體系,複用與擴充 → abstract/virtual + override + 模板方法
  • 多態實現同一介面多種行為 → 基類參考 + 虛擬方法分派 + 介面依賴

三者相輔相成:封裝是基石,繼承是骨架,多態是靈魂。熟練運用它們配合 SOLID 原則,才能寫出「易讀、易改、易測、易擴」的高品質 C# 代碼。

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