型別 · 值與引用
C# 裡 struct 與 class
宣告用的關鍵字不如賦值語義重要:class 變數持有的是指向同一個共享物件的引用;struct 變數持有的是它自己的一份複製。其他一切都由此而來。
class:兩個名字,一個物件
class 變數賦值時複製的是引用,不是物件。兩個名字指向同一個盒子:
class Dog
{
public string Name;
public Dog(string name) { Name = name; }
}
Dog a = new Dog("Rex");
Dog b = a; // b 是同一個物件的第二個名字
b.Name = "Max"; // 通過 b 改名……
Console.WriteLine(a.Name); // ……a 也跟著改名了
class Dog
{
public string Name;
public Dog(string name) { Name = name; }
}
Dog a = new Dog("Rex");
Dog b = new Dog("Rex"); // 一個真正的新物件
b.Name = "Max";
Console.WriteLine(a.Name); // 沒被動過
Console.WriteLine(a == b); // == 比較引用:不同物件 → False
// (內容相同,身份不同)
struct:賦值即複製
struct 把語義反過來。給 struct 變數賦值會複製整個值——從那一刻起兩個變數互不相干:
struct Point
{
public int X;
public Point(int x) { X = x; }
}
Point a = new Point(1);
Point b = a; // 完整複製——b 有自己的儲存
b.X = 99;
Console.WriteLine(a.X); // 1 —— a 從未被碰過
Console.WriteLine(b.X); // 99
誠實說明:瀏覽器引擎目前把 struct 當 class 處理(引用語義)——這段程式在 Visual Studio 裡會像註釋所示列印 1 和 99。概念不變;在引擎長出值語義之前,複製行為請在真實編譯器裡驗證。
這個差異解釋的 gotcha
有沒有過這種經歷:通過一個變數改了欄位,另一個變數竟然也"看到"了變化?那是引用別名。它的映象:在方法裡改了 struct 的欄位,呼叫方的複製沒變?那是值複製。兩者都是各自語義下的正確行為——bug 是你期待了另一種:
| 場景 | class | struct |
|---|---|---|
b = a; 複製的是…… | 引用 | 整個值 |
通過 b 修改會影響 a 嗎? | 會——同一個物件 | 不會——各自的複製 |
a == b 意味著…… | 同一身份 | 欄位值相同(如果定義了 ==) |
| 住在哪裡…… | 堆上,垃圾回收 | 內聯(棧上或在宿主內部) |
到底選哪個?
大多數程式碼庫遵循的實用規則:
- 預設用
class。有身份的實體(使用者、訂單、狗)——身份正是引用語義所建模的東西。 - 考慮
struct:小(約 16–24 位元組以內)、不可變、複製廉價自然的值狀資料:座標、金額、日期、顏色。.NET 庫自己的int、double、bool、DateTime、Guid全是 struct。 - 可變的 struct 是陷阱。一份複製悄悄改自己幾乎從來不是本意——如果 struct 需要變,它多半想成為 class。
一分鐘自測
去練習場跑這段,按執行之前先預測輸出——就是上面的別名演示換了變數名:
Dog x = new Dog("One");
Dog y = x;
y = new Dog("Two");
Console.WriteLine(x.Name); // 預測:One 還是 Two?(引用 vs 物件)
如果你答"One"——把 new Dog("Two") 賦給 y 只是讓 y 的引用改指向,它從不碰 x 指著的那個物件。相關閱讀:var 與顯式型別。