四種參數模式完整對比
| 模式 | 關鍵字 | 記憶體行為 | 初始化要求 | 資料流向 | 典型用途 |
| 值參數 | (無) | 複製值 (值型別) 或複製參考 (參考型別) | 呼叫端必須已指派 | 單向 (進) | 預設、唯讀輸入 |
| 參考參數 | ref | 傳遞變數位址 (別名) | 呼叫端必須已指派 | 雙向 (進/出) | 修改呼叫端變數、大型結構體避免複製 |
| 輸出參數 | out | 傳遞變數位址 (別名) | 呼叫端無需初始化,被呼叫端必須指派 | 單向 (出) | 多傳回值、TryParse 模式 |
| 參數陣列 | params | 建立陣列實例封裝引數 | 呼叫端可提供 0~N 個引數 | 單向 (進) | 可變參數個數、便利呼叫 |
值參數:參考型別的「複製參考」陷阱
void ModifyList(List list) {
list.Add(99); // 修改「共享物件」內容 → 影響呼叫端
list = new List(); // 修改「區域參考」 → 不影響呼叫端
}
var myList = new List { 1, 2 };
ModifyList(myList);
Console.WriteLine(myList.Count); // 3 (內容被修改)
值參數傳遞參考型別時,複製的是參考值,雙方指向同一物件。若需完全隔離,需手動深層複製。
ref 參數:交換兩個變數經典範例
void Swap(ref int x, ref int y) {
int temp = x; x = y; y = temp;
}
int a = 1, b = 2;
Swap(ref a, ref b);
Console.WriteLine($"a={a}, b={b}"); // a=2, b=1
ref 區域變數別名陷阱
void AliasDemo(ref string s1, ref string s2) {
s1 = "Changed";
}
string str = "Original";
AliasDemo(ref str, ref str); // 同一變數傳遞兩次
Console.WriteLine(str); // "Changed" - 兩個參數指向同一儲存位置
out 參數:TryParse 模式標準實作
bool TryParseInt(string input, out int result) {
// 必須在所有路徑指派 result
if (int.TryParse(input, out result)) return true;
result = 0; // 失敗時也要指派
return false;
}
// 使用 (C# 7+ 內建 out 變數宣告)
if (TryParseInt("123", out int value))
Console.WriteLine(value);
params 參數陣列:規則與最佳實務
- 必須是最後一個參數。
- 只能有一個
params 參數。
- 型別必須是一維陣列 (
int[]、string[]),不可為多維陣列 (int[,])。
- 不可與
ref/out 結合。
void Log(params string[] messages) {
foreach (var msg in messages) Console.WriteLine($"[LOG] {msg}");
}
// 多種呼叫方式
Log(); // 空陣列
Log("Single"); // 隱含轉換
Log("A", "B", "C"); // 隱含建立陣列
Log(new[] { "X", "Y" }); // 顯式陣列
現代 C# 進階參數特性
| 特性 | 語法 | 說明 |
| 預設參數值 | void M(int x = 10) | 編譯器在呼叫端插入預設值,非虛擬方法才安全 |
| 命名引數 | M(y: 5, x: 3) | 順序無關,提升可讀性 |
| ref 回傳 | ref int Find(int[] arr, int val) | 回傳陣列元素別名,可直接修改元素 |
| in 參數 | void M(in readonly int x) | 唯讀參考傳遞,避免複製大型結構體 |
| out 變數宣告 | TryParse(s, out int v) | C# 7+ 內建宣告,無需預先宣告 |
設計決策流程圖
- 需修改呼叫端變數? →
ref
- 需輸出多個值且呼叫端無初始值? →
out
- 參數個數不固定? →
params
- 大型結構體唯讀傳遞? →
in (C# 7.2+)
- 預設情況 → 值參數