C# 方法參數預設為「值傳遞」。理解四種修飾詞的語意差異,是撰寫正確、高效程式碼的關鍵。
| 類型 | 關鍵字 | 記憶體行為 | 呼叫端要求 | 被呼叫端要求 | 典型用途 |
|---|---|---|---|---|---|
| 值參數 | (無) | 複製實參值到形參 (值型別複製內容,參考型別複製參考) | 需初始化 | 可讀寫形參,不影響實參 (值型別) | 預設、單向輸入 |
| 參考參數 | ref | 傳遞實參的記憶體位址,形參別名指向同一儲存位置 | 必須初始化 | 可讀寫,變更直接反映實參 | 雙向傳遞、修改呼叫端變數、避免大型結構體複製 |
| 輸出參數 | out | 同 ref,但語意為「僅輸出」 | 無需初始化 | 方法返回前必須指派 | 多返回值、TryParse 模式 |
| 參數陣列 | params | 將零到多個引數封裝為陣列傳遞 | 可傳 0~N 個引數或單一陣列 | 內部視為陣列 T[] | 可變參數列表、String.Format, Console.WriteLine |
void Increment(int x) { x++; } // 只改變區域副本
int a = 5;
Increment(a);
Console.WriteLine(a); // 5,未變
void Swap(ref int x, ref int y)
{
int t = x; x = y; y = t;
}
int a = 1, b = 2;
Swap(ref a, ref b);
Console.WriteLine($"{a}, {b}"); // 2, 1
// 結構體避免複製
struct LargeStruct { public long L1, L2, L3, L4; }
void Modify(ref LargeStruct s) { s.L1 = 99; } // 不複製 32 bytes
// 標準 TryParse 模式
bool TryParseInt(string s, out int result)
{
return int.TryParse(s, out result); // 必須指派 result
}
int num;
if (TryParseInt("123", out num)) Console.WriteLine(num); // 123
// num 呼叫前可未初始化
void PrintAll(params object[] args)
{
Console.WriteLine($"Count: {args.Length}");
foreach (var o in args) Console.Write(o + " ");
Console.WriteLine();
}
PrintAll(); // Count: 0
PrintAll(1, 2, 3); // Count: 3
PrintAll("A", "B", "C", "D"); // Count: 4
PrintAll(new object[] { 1, 2 }); // Count: 2 (傳陣列)
class Person { public string Name; }
void ChangeName(Person p) { p.Name = "Bob"; } // 修改物件內容 ✅
void ReplacePerson(Person p) { p = new Person(); } // 只換區域參考 ❌ 不影響實參
void ReplacePersonRef(ref Person p) { p = new Person(); } // 換掉實參指向 ✅
void M(int x) { } // 1. 精確匹配
void M(params int[] x) { } // 2. params 擴展匹配
void M(ref int x) { } // 僅當呼叫端加 ref 才匹配
M(5); // 呼叫 (1)
M(1,2,3); // 呼叫 (2)
int i=0; M(ref i); // 呼叫 (3)
params 參數,且必須是最後一個參數。ref/out 共存:params ref int[] ❌。void Log(string msg) + void Log(string fmt, params object[] args))。in 修飾詞 (補充)// 唯讀參考傳遞:避免複製大型結構體,且禁止方法內修改
void ReadOnly(in LargeStruct s)
{
// s.L1 = 10; // 編譯錯誤:無法指派唯讀變數
Console.WriteLine(s.L1);
}
ref (輸入輸出) 或 out (純輸出)。out 或 ValueTuple ((bool, int)) / 自訂 Result<T>。params 簡化呼叫端;效能敏感處提供陣列多載。in (C# 7.2+),兼顧效能與安全。ref/out,增加認知負擔與副作用風險。精準選擇參數模式,能讓方法契約更清晰、執行效率更優、Bug 更少。