CodeGym /課程 /C# SELF /實戰應用封裝

實戰應用封裝

C# SELF
等級 17 , 課堂 4
開放

1. 控制存取權限

封裝 不是「什麼都藏起來」,而是 給出可控的存取權限

很多新手會以為封裝就是把所有欄位都設成 private。沒錯,欄位通常會 private,但這不是全部。重點不是單純 隱藏 資料,而是 管理存取

有時候這代表完全禁止修改(像是 get-only 屬性)。有時候允許讀寫但要驗證。有時候只允許 class 內部讀寫(public Type Property { get; private set; })。

封裝讓你可以為你的物件訂「遊戲規則」:決定怎麼改它、保證每次改動都會讓它保持有效狀態。

在實際專案裡,尤其是大團隊,沒有封裝就會亂成一團。每個人都能直接改別人的物件內部狀態,錯誤和衝突會爆炸多。封裝讓每個模組(class)都能自給自足,自己負責自己的資料。

這是 SOLID 原則的基石之一,特別是 單一職責原則 (Single Responsibility Principle)開放封閉原則 (Open/Closed Principle),之後你會學到。現在只要記住,封裝讓你的程式碼更強壯、彈性又好懂、好維護。


// 封裝的目標:在重要資料外面蓋一層隱形柵欄
// 沒人能不小心從外部「搞砸」!
封裝保護物件不被亂改

封裝就像在你的物件重要資料外面蓋一層隱形柵欄,沒人能不小心搞砸。你總不希望你的狗突然有負的年齡吧?封裝就是在幫你顧這個。它也讓 class 用起來很簡單:其他工程師不用進去研究內部怎麼寫,只要用你給的東西就好。最酷的是,明天你想改 class 內部,比如把年齡從單純數字改成存生日,外部根本不會發現,因為介面沒變。這才是封裝真正的價值!

2. 封裝的好處

為什麼這對你的專案和職涯這麼重要?

資料完整性 (Data Integrity):
封裝可以確保物件的資料永遠是正確、合理的狀態。這會大幅減少 bug。面試被問 OOP,如果你能好好解釋封裝怎麼幫助資料完整性,超加分!

彈性和好維護 (Flexibility & Maintainability):
想像一下,你一開始把狗的年齡存成 int Age。過一年,老闆說:「我們要知道狗的生日,不要年齡!」

沒封裝的話: 如果 Agepublic int,你程式裡一堆地方都直接用 myDog.Age。你得全部改成 DateTime,還要重寫年齡計算邏輯。超痛苦!

有封裝的話: 如果你有 private int _age;public int Age { get; set; },你只要把內部欄位改成 private DateTime _dateOfBirth;,然後重寫 getset 的邏輯,讓 Age 變成用生日算出來。外部用 myDog.Age 的程式 完全不用改,因為他們只碰到 public 介面 Age,沒直接碰欄位。這就叫 降低耦合 (loose coupling)

看到沒,超神奇?我們換了 class「內餡」,但「包裝」(public 介面)沒變!

更好 debug (Easier Debugging):
如果物件資料出問題,有好好封裝的話,你知道問題只會出在 class 自己的方法或 public 屬性。找 bug 只要看一個 class,不用全世界找。

API 設計更棒:
封裝 class 時,你會明確定義哪些是 public「合約」(API),哪些是內部細節。這讓你的 class 介面乾淨、可預期、好懂。API 越清楚,別人(或你未來自己)用你的 code 就越輕鬆。

安全性 (Security):
雖然 C# 不是像 C++ 那種超講究安全的系統語言,封裝還是很重要。它讓你能控制誰、怎麼碰你的資料,避免亂改或偷看機密資料(如果有的話)。

3. C# 怎麼做封裝?

在 C# 裡,封裝主要靠這幾招:

存取修飾詞 (private, public 等):
我們把 class 欄位設成 private,這樣外部不能直接改。這就是 資訊隱藏 (information hiding)
我們把 方法和屬性設成 public,這樣才能給外部可控的存取權限。這就是 public 介面

屬性 (Properties):
你學過,屬性就是 get(讀)和 set(寫)方法的語法糖。它讓你把欄位藏起來,但還是能透過 public 外觀存取。最重要的是,set 裡可以加驗證邏輯!

範例:有封裝的 Dog class

先來寫個 錯誤示範(沒封裝):


public class Dog
{
    public string Name;
    public int Age;

    public void Bark()
    {
        Console.WriteLine($"{Name} 說:汪!");
    }
}

這種寫法,其他 class 可以隨便改狗的名字和年齡。例如:

Dog dog = new Dog();
dog.Name = "";      // 可以設成空名字!
dog.Age = -100;     // 可以讓狗變超「古老」

現實生活很少這樣,但 code 裡常常發生,如果你沒想過封裝。

保護資料:欄位設成 private

把欄位設成 private。這樣除了 class 自己,誰都不能直接改:


public class Dog
{
    private string _name;
    private int _age;

    public void Bark()
    {
        Console.WriteLine($"{_name} 說:汪!");
    }
}

這樣就不能這樣用了:

Dog dog = new Dog();
dog._name = "Rex"; // 編譯錯誤!

透過屬性 (Properties) 存取資料

但你還是想知道狗的名字(比如要印出來),有時也要改(換主人或個性變了)。這時就用屬性!


public class Dog
{
    private string _name;
    private int _age;

    public string Name
    {
        get { return _name; }
        set
        {
            // 加驗證:名字不能是空的
            if (string.IsNullOrWhiteSpace(value))
            {
                Console.WriteLine("錯誤:名字不能是空的!");
            }
            else
            {
                _name = value;
            }
        }
    }

    public int Age
    {
        get { return _age; }
        set
        {
            // 實際一點:年齡不能是負的!
            if (value < 0)
            {
                Console.WriteLine("錯誤:年齡不能是負的!");
            }
            else
            {
                _age = value;
            }
        }
    }

    public void Bark()
    {
        Console.WriteLine($"{_name} 說:汪!");
    }
}

現在資料有保護,但你還是可以透過 可控介面 用它們:

Dog dog = new Dog();
dog.Name = "Barbos";      // OK!
dog.Age = 3;

dog.Name = "";            // 會顯示錯誤,欄位不會變!
dog.Age = -1;             // 又錯誤

自動屬性

如果你不需要額外驗證邏輯,可以不用寫欄位,直接用自動屬性:


public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Bark()
    {
        Console.WriteLine($"{Name} 說:汪!");
    }
}

這時 NameAge 其實也「被封裝」了——你只能透過屬性讀寫,不能直接碰欄位。

4. 封裝行為:只把需要的方法公開

封裝不只資料,還有方法!有些方法是 class 內部的小齒輪,根本不該給外部看。

範例:


public class Dog
{
    // 只給內部用
    private void WagTail()
    {
        Console.WriteLine("狗在搖尾巴。");
    }

    // 給大家用
    public void Bark()
    {
        WagTail(); // 在 class 裡呼叫隱藏方法
        Console.WriteLine("汪!");
    }
}

現在除了狗自己,誰都不能直接讓牠搖尾巴:

Dog dog = new Dog();
dog.WagTail();     // 錯誤!方法是 private。
dog.Bark();        // Bark 裡面會呼叫 WagTail

總結:欄位、方法、屬性和可見性

來整理一下:

class 部分 可以設 private 嗎? 可以設 public 嗎? 為什麼要限制存取?
欄位 可以 可以(但不要!) 保護資料
方法 可以 可以 隱藏實作細節
屬性 可以 可以 控制讀寫

建議:欄位都設 private,只透過屬性或方法給外部用。

1
問卷/小測驗
屬性 (Properties),等級 17,課堂 4
未開放
屬性 (Properties)
屬性跟存取修飾詞
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION