☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
C# 基礎 中級

🏗️ 物件導向程式設計(OOP)

📌 什麼是物件導向?

物件導向就像用積木蓋房子

  • 類別(Class) = 積木的設計圖(藍圖)
  • 物件(Object) = 根據設計圖做出的實際積木
  • 一張設計圖可以做出很多個積木

📌 類別與物件

// 定義一個「學生」類別(設計圖)
class Student
{
    // 屬性(Properties)— 學生有什麼特徵
    public string Name { get; set; } = "";  // 姓名(自動屬性)
    public int Age { get; set; }              // 年齡
    public double GPA { get; set; }           // 成績

    // 建構子(Constructor)— 建立物件時自動執行
    public Student(string name, int age, double gpa)
    {
        Name = name;   // 設定姓名
        Age = age;     // 設定年齡
        GPA = gpa;     // 設定成績
    }

    // 無參數建構子
    public Student() { }  // 什麼都不做,使用預設值

    // 方法(Methods)— 學生能做什麼
    public void Introduce()  // 自我介紹的方法
    {
        Console.WriteLine($"我是 {Name},{Age} 歲,GPA {GPA}");
    }

    public bool IsPassing() => GPA >= 2.0;  // 是否及格
}

// 建立物件(根據設計圖做出實際的積木)
Student s1 = new Student("小明", 20, 3.5);  // 用建構子建立
s1.Introduce();  // 輸出:我是 小明,20 歲,GPA 3.5

Student s2 = new Student  // 用物件初始化語法建立
{
    Name = "小美",     // 設定姓名
    Age = 19,            // 設定年齡
    GPA = 3.8            // 設定成績
};
Console.WriteLine(s2.IsPassing());  // 輸出:True

📌 屬性(Properties)

class Product
{
    // 自動屬性(最常用)
    public string Name { get; set; } = "";  // 可讀可寫
    public decimal Price { get; set; }        // 可讀可寫

    // 唯讀屬性(只有 get)
    public DateTime CreatedAt { get; } = DateTime.Now;  // 建立後不能改

    // 計算屬性(根據其他屬性動態計算)
    public decimal Tax => Price * 0.05m;  // 稅金 = 價格 * 5%
    public decimal Total => Price + Tax;  // 總價 = 價格 + 稅金

    // 有驗證邏輯的屬性
    private int _stock;  // 私有欄位(backing field)
    public int Stock     // 公開屬性
    {
        get => _stock;   // 取得庫存數量
        set              // 設定庫存數量(帶驗證)
        {
            if (value < 0)  // 庫存不能是負數
                throw new ArgumentException("庫存不能為負數");
            _stock = value; // 驗證通過才設定
        }
    }
}

var product = new Product { Name = "筆電", Price = 30000 }; // 建立商品
Console.WriteLine($"商品:{product.Name}");        // 輸出:商品:筆電
Console.WriteLine($"稅金:{product.Tax}");         // 輸出:稅金:1500
Console.WriteLine($"總價:{product.Total}");       // 輸出:總價:31500

📌 繼承(Inheritance)

// 基底類別(父類別)— 動物
class Animal
{
    public string Name { get; set; } = "";  // 動物名字
    public int Age { get; set; }              // 動物年齡

    // virtual 表示子類別可以覆寫這個方法
    public virtual void Speak()  // 說話(虛擬方法)
    {
        Console.WriteLine("...");  // 預設沒有聲音
    }

    public void Eat()  // 吃東西(一般方法)
    {
        Console.WriteLine($"{Name} 正在吃東西");
    }
}

// 衍生類別(子類別)— 狗繼承動物
class Dog : Animal  // Dog 繼承 Animal(用 : 表示繼承)
{
    public string Breed { get; set; } = "";  // 品種(Dog 獨有的屬性)

    // override 覆寫父類別的 virtual 方法
    public override void Speak()  // 狗的叫聲
    {
        Console.WriteLine($"{Name} 說:汪汪!");  // 狗會汪汪叫
    }

    public void Fetch()  // 撿球(Dog 獨有的方法)
    {
        Console.WriteLine($"{Name} 去撿球了!");
    }
}

// 衍生類別 — 貓繼承動物
class Cat : Animal  // Cat 繼承 Animal
{
    public override void Speak()  // 貓的叫聲
    {
        Console.WriteLine($"{Name} 說:喵~");  // 貓會喵喵叫
    }
}

// 使用
Dog dog = new Dog { Name = "小白", Age = 3, Breed = "柴犬" };
dog.Speak();  // 輸出:小白 說:汪汪!
dog.Eat();    // 輸出:小白 正在吃東西(繼承自 Animal)
dog.Fetch();  // 輸出:小白 去撿球了!

📌 抽象類別與介面

// 抽象類別(Abstract Class)— 不能直接建立物件
abstract class Shape  // 形狀(抽象概念)
{
    public string Color { get; set; } = "黑色";  // 一般屬性

    // 抽象方法:只有宣告,沒有實作(子類別必須實作)
    public abstract double GetArea();  // 取得面積

    // 一般方法:有完整的實作
    public void PrintInfo()  // 印出資訊
    {
        Console.WriteLine($"顏色:{Color},面積:{GetArea()}");
    }
}

class Circle : Shape  // 圓形繼承形狀
{
    public double Radius { get; set; }  // 半徑

    // 必須實作抽象方法
    public override double GetArea() => Math.PI * Radius * Radius; // 圓面積公式
}

class Rectangle : Shape  // 矩形繼承形狀
{
    public double Width { get; set; }   // 寬
    public double Height { get; set; }  // 高

    public override double GetArea() => Width * Height;  // 矩形面積公式
}

// 介面(Interface)— 定義「能做什麼」的契約
interface IMovable  // 介面名稱慣例以 I 開頭
{
    void Move(int x, int y);  // 移動(只有宣告,沒有實作)
    double Speed { get; set; } // 速度屬性
}

interface IResizable  // 可調整大小的介面
{
    void Resize(double factor);  // 調整大小
}

// 一個類別可以實作多個介面(但只能繼承一個類別)
class GameCharacter : IMovable, IResizable  // 遊戲角色
{
    public string Name { get; set; } = "";   // 角色名稱
    public double Speed { get; set; } = 1.0;   // 移動速度

    public void Move(int x, int y)  // 實作 IMovable 的 Move
    {
        Console.WriteLine($"{Name} 移動到 ({x}, {y})");
    }

    public void Resize(double factor)  // 實作 IResizable 的 Resize
    {
        Console.WriteLine($"{Name} 大小變為 {factor} 倍");
    }
}

📌 封裝與存取修飾詞

class BankAccount  // 銀行帳戶
{
    // private — 只有這個類別內部能存取
    private decimal _balance;  // 餘額(私有,外部不能直接碰)

    // public — 任何地方都能存取
    public string Owner { get; set; } = "";  // 帳戶持有人

    // protected — 這個類別和子類別能存取
    protected string AccountNumber { get; set; } = "";  // 帳號

    // internal — 同一個專案內能存取
    internal DateTime CreatedDate { get; set; } = DateTime.Now;

    // 建構子
    public BankAccount(string owner, decimal initialBalance)
    {
        Owner = owner;           // 設定持有人
        _balance = initialBalance; // 設定初始餘額
    }

    // 公開方法控制對私有資料的存取
    public decimal GetBalance() => _balance;  // 查詢餘額

    public void Deposit(decimal amount)  // 存款
    {
        if (amount <= 0) // 驗證金額
            throw new ArgumentException("存款金額必須大於 0");
        _balance += amount; // 增加餘額
        Console.WriteLine($"存入 {amount},餘額 {_balance}");
    }

    public void Withdraw(decimal amount)  // 提款
    {
        if (amount > _balance) // 檢查餘額是否足夠
            throw new InvalidOperationException("餘額不足");
        _balance -= amount; // 扣除餘額
        Console.WriteLine($"提領 {amount},餘額 {_balance}");
    }
}

var account = new BankAccount("小明", 1000);  // 建立帳戶
account.Deposit(500);    // 存入 500 → 餘額 1500
account.Withdraw(200);   // 提領 200 → 餘額 1300
// account._balance = 0; // ❌ 編譯錯誤!_balance 是 private

📌 多型(Polymorphism)

// 多型:同一個方法呼叫,不同物件有不同行為
List<Animal> animals = new List<Animal>  // 建立動物清單
{
    new Dog { Name = "小白" },  // 加入一隻狗
    new Cat { Name = "咪咪" },  // 加入一隻貓
    new Dog { Name = "大黃" },  // 再加入一隻狗
};

foreach (Animal animal in animals) // 走訪每隻動物
{
    animal.Speak(); // 同樣是 Speak(),但每隻動物的叫聲不同!
}
// 輸出:
// 小白 說:汪汪!
// 咪咪 說:喵~
// 大黃 說:汪汪!

// 多型讓你用「基底類別」的變數來操作「衍生類別」的物件
Animal myPet = new Dog { Name = "旺財" };  // Animal 變數裝 Dog 物件
myPet.Speak();  // 輸出:旺財 說:汪汪!(執行的是 Dog 的版本)

// is 和 as 運算子
if (myPet is Dog dogRef)  // 檢查 myPet 是否是 Dog,並轉型
{
    dogRef.Fetch();  // 可以使用 Dog 獨有的方法
}

🤔 我這樣寫為什麼會錯?

❌ 錯誤:忘記 override

class MyDog : Animal
{
    // ❌ 用 new 只是隱藏父類別的方法,不是覆寫
    // 多型時不會正確呼叫
    public new void Speak() { Console.WriteLine("汪!"); }
}

// 當用 Animal 型別的變數時:
Animal a = new MyDog { Name = "test" };
a.Speak(); // 輸出:...(呼叫的是 Animal 的版本,不是 MyDog 的!)

// ✅ 正確:用 override 覆寫 virtual 方法
class MyDogFixed : Animal
{
    public override void Speak() { Console.WriteLine("汪!"); }
}

❌ 錯誤:想建立抽象類別的實例

// ❌ 錯誤:抽象類別不能直接建立物件
// Shape shape = new Shape(); // 編譯錯誤!

// ✅ 正確:建立具體子類別的物件
Shape shape = new Circle { Radius = 5 };  // 用子類別

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊