在 C# 開發中,ref 關鍵字常讓開發者感到困惑。理解它的關鍵在於掌握 C# 的類型系統——特別是值類型與引用類型在參數傳遞時的行為差異。本文將透過對比 C++ 指標機制,配合實際程式碼範例,徹底釐清 ref 的正確用法與核心邏輯。
C# 的資料類型分為兩大類:
int、long、double 等)與結構(struct)。傳遞時會進行複製操作。class 類型。傳遞時僅複製引用(類似 C++ 指標),指向同一個堆上物件。特別注意:C# 的 struct 是值類型,這與 C++ 中 struct 預設為引用語意(與 class 僅存取權限不同)有本質區別,這是極易被忽略的陷阱。
MSDN 經典範例展示了 char(值類型)在有無 ref 時的差異:
public static void TestRef(ref char i) { i = 'b'; }
public static void TestNoRef(char i) { i = 'c'; }
static void Main() {
char i = 'a';
TestRef(ref i); // 輸出 'b'
TestNoRef(i); // 輸出 'b'(原值已被 TestRef 修改)
}
無 ref 時,方法操作的是副本;有 ref 時,直接操作原變數的記憶體位址。
當參數為引用類型(如自訂 MyClass)時,行為更微妙。請看下列四種測試方法:
public class MyClass { public int Value; }
public static void TestRef(ref MyClass m) { m.Value = 10; }
public static void TestNoRef(MyClass m) { m.Value = 20; }
public static void TestCreateRef(ref MyClass m) { m = new MyClass(); m.Value = 100; }
public static void TestCreateNoRef(MyClass m) { m = new MyClass(); m.Value = 200; }
執行序列與輸出:
MyClass m = new MyClass(); m.Value = 1;
TestRef(ref m); // 輸出 10 (修改原物件屬性)
TestNoRef(m); // 輸出 20 (修改同一物件屬性)
TestCreateRef(ref m); // 輸出 100 (ref 允許替換呼叫端的參考)
TestCreateNoRef(m); // 輸出 100 (無 ref,呼叫端參考不變,仍指向 100 的物件)
將上述 C# 程式碼對應到 C++ 指標操作,邏輯瞬間清晰:
TestRef(ref MyClass m) ⇔ void TestRef(MyClass** m):雙重指標,可修改指標本身指向TestNoRef(MyClass m) ⇔ void TestNoRef(MyClass* m):單指標,只能修改物件內容TestCreateRef 分配新物件並指向它,呼叫端指標跟著變;TestCreateNoRef 只是讓區域指標指向新物件,呼叫端不受影響。ref 才能修改原值。ref 才能替換呼叫端的參考指向。ref 相當於「指標的指標」或「引用的引用」,能統一解釋所有行為。ref 應謹慎使用,優先考慮回傳值或 out 參數,保持 API 簡潔。若想更深入驗證,可嘗試將範例中的 class 改為 struct,觀察值類型語意下的差異,這能加深對 C# 類型系統的理解。