iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Modern Web

現在就學C# 與 ASP.NET Core系列 第 11 篇

Day 11|Encapsulation 與 Property:讓 Class 管理自己的資料

  • 分享至 

  • xImage
  •  

Day 10 我們學會使用 Class,把相關的資料與行為組織在一起。

例如:

class Order
{
    public string Product = "";
    public decimal Price;
    public int Quantity;

    public decimal CalculateTotal()
    {
        return Price * Quantity;
    }
}

但目前有一個問題:

order.Price = -100m;
order.Quantity = -10;

Class 外部可以直接修改資料,Order 本身無法控制資料是如何被修改的。

因此今天要解決的問題是:

Class 如何控制自己的資料,可以怎麼被外部讀取與修改?

這就是:

Encapsulation(封裝) 與 Property(屬性)


今天會學到

  • Encapsulation
  • Access Modifier
  • public
  • private
  • Property
  • get
  • set
  • private set

今天最重要的概念:

Public Field
     ↓
外部可以直接存取
     ↓
  Property
分開讀取與設定
     ↓
 private set
限制外部修改
     ↓
Class 掌握資料的存取方式
     ↓
Encapsulation

1. 為什麼需要 Encapsulation?

Day 10 的 Order:

class Order
{
    public string Product = "";
    public decimal Price;
    public int Quantity;
}

因為 Field 都是 public,所以 Class 外部可以直接:

order.Price = -100m;
order.Quantity = -10;

也就是外部程式可以直接決定 Object 裡的資料。

真正的問題是:

Object 的資料不應該完全交給外部任意操作。

我們希望改成:

外部程式
    ↓
Class 提供的方式
    ↓
存取或修改資料

這就是 Encapsulation 的核心。

可以先簡單理解:

Encapsulation 就是讓 Class 自己管理資料,而不是讓外部程式隨意操作。


2. Access Modifier:控制誰可以存取

要控制 Class 的資料,首先要認識:

Access Modifier(存取修飾詞)

目前先掌握:

public
private

public

例如:

public decimal Price;

public 代表 Class 外部也可以存取。

所以:

Console.WriteLine(order.Price);

order.Price = 500m;

都可以。


private

如果改成:

private decimal _price;
private int _quantity;

private 代表:

只有目前 Class 內部可以存取。

例如 Class 內部可以:

class Order
{
    private decimal _price;
    private int _quantity;

    public decimal CalculateTotal()
    {
        return _price * _quantity;
    }
}

但 Class 外部不能直接:

order._price;

簡單整理:

Access Modifier Class 內部 Class 外部
public ✓ ✓
private ✓ ✗

其他 Access Modifier 之後遇到時再介紹。


3. 全部改成 private 就好了嗎?

如果:

class Order
{
    private string _product = "";
    private decimal _price;
    private int _quantity;
}

外部確實不能直接修改資料。

但也出現另一個問題:

Console.WriteLine(order._price);

連正常讀取都做不到。

實際上我們可能希望:

Price

外部讀取
✓

外部直接修改
✗

也就是:

讀取與修改需要分開控制。

這就是 Property 出現的原因。


4. Property:控制資料如何被存取

Property(屬性)是 Class 的一種成員。

可以先理解成:

Property 用來控制資料如何被讀取或設定。

例如:

public decimal Price
{
    get;
    set;
}

其中:

get
→ 讀取

set
→ 設定

所以:

Console.WriteLine(order.Price);

會透過 get 取得資料。

而:

order.Price = 500m;

會透過 set 設定資料。


5. Auto-implemented Property

剛才的 Property:

public decimal Price
{
    get;
    set;
}

通常可以簡寫成:

public decimal Price { get; set; }

這稱為:

Auto-implemented Property(自動實作屬性)

因此 Day 10 的:

public string Product = "";
public decimal Price;
public int Quantity;

可以改成:

public string Product { get; set; } = "";
public decimal Price { get; set; }
public int Quantity { get; set; }

目前不用深入 Compiler 如何保存資料。

先記住:

{ get; set; }

get
→ 可以讀取

set
→ 可以設定

即可。


6. Field 與 Property 有什麼不同?

Field:

public decimal Price;

Property:

public decimal Price { get; set; }

整理如下:

Field Property
範例 public decimal Price; public decimal Price { get; set; }
用途 直接保存資料 提供存取資料的方式
對外提供資料 通常不優先使用 一般較常使用

初學階段先記:

Class 對外提供資料時,通常會優先看到 Property,而不是 Public Field。


7. private set:可以讀,但不能直接修改

雖然我們改成:

public decimal Price { get; set; }

但外部仍然可以:

order.Price = -100m;

因為 set 仍然是公開的。

如果希望:

外部讀取
✓

外部直接修改
✗

Class 內部修改
✓

可以改成:

public decimal Price { get; private set; }

現在外部可以:

Console.WriteLine(order.Price);

但不能:

order.Price = -100m;

因為:

get
→ public

set
→ private

可以理解成:

外部
 │
 ├── 讀取 Price
 │      ✓
 │
 └── 修改 Price
        ✗

Order Class
 │
 └── 修改 Price
        ✓

所以:

private set** 代表外部可以讀取 Property,但只有 Class 內部可以設定它。**

要注意:

private set** 解決的是「誰可以修改」的問題,不代表資料本身一定合法。**

例如 Class 內部仍然可以設定:

Price = -100m;

資料是否合法,之後介紹資料驗證與 Exception 時再進一步處理。


8. Constructor 可以設定 private set

把 Order 改成:

class Order
{
    public string Product { get; private set; }
    public decimal Price { get; private set; }
    public int Quantity { get; private set; }

    public Order(
        string product,
        decimal price,
        int quantity)
    {
        Product = product;
        Price = price;
        Quantity = quantity;
    }
}

雖然 Class 外部不能:

order.Price = 500m;

但 Constructor 位於 Order Class 內部,因此:

Price = price;

可以正常設定。

建立 Object:

Order order =
    new Order("TXF", 250.5m, 5);

流程:

外部
  ↓
new Order(...)
  ↓
Constructor
  ↓
Order 自己初始化資料

建立完成後,外部可以讀取:

Console.WriteLine(order.Product);
Console.WriteLine(order.Price);
Console.WriteLine(order.Quantity);

但不能直接修改:

order.Price = 500m;

9. Property 不等於 Encapsulation

這是一個很重要的觀念。

使用:

public decimal Price { get; set; }

雖然已經是 Property,但外部仍然可以:

order.Price = -100m;

所以:

使用 Property,不代表已經做好 Encapsulation。

真正重要的是:

Class 是否有控制外部可以如何存取與修改資料。

例如:

public decimal Price { get; private set; }

就限制:

外部
→ 可以讀取

外部
→ 不能直接修改

Class 內部
→ 可以修改

因此可以這樣理解:

Property
→ 語法工具

Encapsulation
→ 設計目的

10. 完整的 Order Class

把今天的內容整理起來:

class Order
{
    public string Product { get; private set; }
    public decimal Price { get; private set; }
    public int Quantity { get; private set; }

    public Order(
        string product,
        decimal price,
        int quantity)
    {
        Product = product;
        Price = price;
        Quantity = quantity;
    }

    public decimal CalculateTotal()
    {
        return Price * Quantity;
    }
}

建立:

Order order =
    new Order("TXF", 250.5m, 5);

讀取資料:

Console.WriteLine(order.Product);
Console.WriteLine(order.Price);
Console.WriteLine(order.Quantity);

計算總額:

Console.WriteLine(
    order.CalculateTotal()
);

但外部不能:

order.Price = 500m;

現在 Order 可以理解成:

Order
│
├── Properties
│   ├── Product
│   ├── Price
│   └── Quantity
│
├── Constructor
│   └── 初始化資料
│
└── Method
    └── CalculateTotal()

從 Day 10 到 Day 11 的變化:

Day 10

Public Field
     ↓
外部可以直接存取

Day 11

Property
     ↓
private set
     ↓
Class 控制資料的存取方式

今日練習

建立新的 Console Project:

dotnet new console -n Day11Practice

進入 Project:

cd Day11Practice

建立:

class Product
{
    public string Name;
    public decimal Price;
}

練習一:改成 Property

把:

public string Name;
public decimal Price;

改成 Auto Property。


練習二:限制修改權限

讓:

Name
Price

符合:

外部可以讀取
✓

外部不能直接修改
✗

提示:

private set

練習三:使用 Constructor 初始化

加入 Constructor,讓下面的程式可以建立 Product:

Product product =
    new Product("Keyboard", 1500m);

接著輸出:

Console.WriteLine(product.Name);
Console.WriteLine(product.Price);

最後嘗試:

product.Price = 1800m;

觀察為什麼無法直接修改。


Day 11 小結

今天我們解決的問題是:

Class 如何控制自己的資料,可以怎麼被外部讀取與修改?

核心就是:

Encapsulation


Access Modifier

public
→ Class 外部可以存取

private
→ 只有 Class 內部可以存取

Property

例如:

public decimal Price { get; set; }

其中:

get → 讀取

set → 設定

private set

public decimal Price { get; private set; }

代表:

外部讀取
✓

外部直接修改
✗

Class 內部修改
✓

上一篇
Day 10|Class 與 Object:把相關資料與行為組織在一起
下一篇
Day 12|Nullable Reference Types:理解 null 與安全存取
系列文
現在就學C# 與 ASP.NET Core 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言