CodeGym /課程 /C# SELF /封裝與存取修飾詞

封裝與存取修飾詞

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

1. 前言

想像你在設計一台現代汽車。裡面有超多複雜零件、敏感電子設備、細緻的設定。很明顯,沒有人應該隨便打開引擎控制單元亂轉螺絲。如果大家都亂動,車子可能會變得很怪,還要你來檢查到底是誰弄壞了什麼。

寫程式也是一樣。當你寫一個 class,你會想保護它的內部,不讓別人亂動重要資料,或是去碰那些外部根本不該知道的方法。這就是 封裝 的重點——物件導向程式設計三大原則之一。

封裝可以把實作細節藏起來,明確規定哪些東西可以「給外面用」,哪些要留在裡面。就像你只讓使用者打開手套箱,但引擎蓋鎖得死死的。class 自己決定哪些資料要給外部 code 看,哪些要保密。這樣 code 就更穩、更有邏輯,也更好維護。

嚴格來說,封裝 就是把資料(欄位)和跟它有關的行為(方法)合在一個結構裡,同時限制直接存取物件內部元件的方式。

2. 存取修飾詞:守護自己的地盤

在 C#(還有其他 OOP 語言)裡,封裝 是靠 存取修飾詞 來實現的。這些「標籤」決定 class 的哪些成員(欄位、方法、屬性等等)可以被外部看到,哪些只能在內部用。

我們之前有碰過,這裡再提醒一下,順便補充一下常見的修飾詞:

修飾詞 可存取範圍...
public
大家都可以!專案裡任何 code(如果是 library,外部也可以)
private
只有這個 class 裡面可以用
protected
這個 class 跟它所有繼承的子類都可以用
internal
只有這個組件(專案)裡面可以用
protected internal
要嘛在這個組件裡、要嘛在繼承的子類裡都可以用
private protected
只有這個組件裡的繼承子類可以用

這堂課我們主要看最常用的:public、private,還有稍微提一下 protected,不會讓你腦袋爆炸。

分清楚內部跟外部

來寫個 Dog class:


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

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

這裡所有欄位和方法都是 public。意思是,任何人都可以改狗的名字或年齡:

Dog rex = new Dog("雷克斯", 5);
rex.Name = "小黑"; // 突然換身份!
rex.Age = -999;     // 年齡出大事

但 Name 跟 Age 這兩個欄位超重要,不能隨便給人亂改。

這樣寫很危險

如果全部欄位都 public,class 的邏輯很容易被破壞:外部亂動就可能讓物件變得不合法。比如給狗設負數年齡,或名字設成 "%$#!??" 這種鬼東西。

3. 把欄位藏起來:用 private

通常 class 的欄位都會設成 私有(private)。這樣只有 class 自己能改,外面完全沒辦法動。


public class Dog
{
    private string name;
    private int age;

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

現在如果外部想直接動欄位:

Dog rex = new Dog("雷克斯", 5);
rex.name = "小黑";  // 編譯錯誤

編譯器直接跟你說:「沒門!」

為什麼要這麼嚴?彈性去哪了?

「彈性」是靠特別的方法或 屬性(下一堂課會講)來控制的,這樣才能照規則改資料。

4. 封裝實戰:加點檢查

假設我們想讓狗的年齡不能設成負數:


public class Dog
{
    private string name;
    private int age;

    public Dog(string name, int age)
    {
        this.name = name;
        if (age >= 0)
            this.age = age;
        else
            this.age = 0; // 不讓你設奇怪的年齡
    }

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

    // 安全改年齡的方法
    public void SetAge(int newAge)
    {
        if (newAge >= 0)
            age = newAge;
        // 也可以加 else:提示值不對
    }
}

現在外部不能直接亂動欄位。要改年齡只能用特定方法,還會檢查值對不對。

5. 欄位 vs 存取方法(getter/setter)

這種把欄位設 private,再提供方法來操作的方式,叫做 資料封裝(data encapsulation)。要讀欄位就寫 getter,要改就寫 setter。


public class Dog
{
    private string name;

    public Dog(string name)
    {
        this.name = name;
    }

    public string GetName()
    {
        return name;
    }

    public void SetName(string newName)
    {
        // 這裡可以加名字檢查
        name = newName;
    }
}

不過! C# 有 properties 之後,這種方法就比較少用了——屬性讓 code 更乾淨好用(下堂課會細講)。

6. 方法和 class 也有存取修飾詞

不只欄位!方法(class 的成員函式)甚至 class 本身都可以設存取修飾詞。

只給 class 內部用的方法(像是內部邏輯的小工具),通常都設 private。

公開的方法——這就是 class 的介面(不是 interface 關鍵字那個),也就是給 class 使用者用的東西。

7. 封裝常見錯誤

錯誤一:什麼都設 public。
以為越多東西能用越方便,其實這樣會讓 class 內部暴露,外部亂改就很危險,尤其專案大了更容易出事。

錯誤二:改了存取修飾詞,外部 code 爆炸。
重構時不小心改到方法或欄位的修飾詞,結果其他 code 都不能編譯或行為怪怪的。公開 API 特別要小心這點。

錯誤三:搞混區域變數和 class 欄位。
有時候會忘記方法裡宣告的變數只活在那個方法裡,而 class 欄位整個 class 都能用。如果名字一樣更容易出 bug。

錯誤四:不敢用 private 跟 protected。
很多人怕設太嚴,之後用不到。但封裝就是要把多餘的東西藏起來,只把真的需要的東西給外面用。

2
任務
C# SELF, 等級 17, 課堂 0
上鎖
透過方法控制存取權限
透過方法控制存取權限
2
任務
C# SELF, 等級 17, 課堂 0
上鎖
使用封裝並檢查資料正確性
使用封裝並檢查資料正確性
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION