| 修飾詞 | 方向 | 呼叫端初始化 | 方法內必須指派 | 可變參數 | 多載區別 |
|---|---|---|---|---|---|
params | 輸入 | N/A | 否 | 是 (陣列) | 視為陣列參數 |
ref | 雙向 | 必須 | 否 (可選) | 否 | 與 out 不能互相多載 |
out | 輸出 | 不需 | 是 (所有路徑) | 否 | 與 ref 不能互相多載 |
| (無) | 輸入 | 必須 | 否 | 否 | 與 ref/out 可多載 |
public static void Print(params object[] args)
{
Console.WriteLine(string.Join(" ", args.Select(a => a?.ToString() ?? "null")));
}
// 呼叫方式
Print(); // 空陣列
Print(1, 2, 3); // 隱式建立 int[] → object[]
Print("A", "B", "C"); // 字串陣列
Print(new object[] { 1, "x", 3.14 }); // 顯式陣列
int[] nums = { 10, 20 };
Print(nums); // 直接傳遞陣列 (無複製)
規則:僅能出現在參數列表最後,每個方法最多一個。
// 實值型別:交換值
public static void Swap(ref int a, ref int b)
{ int t = a; a = b; b = t; }
// 參考型別:修改內容 vs 替換參考
public static void Append(ref StringBuilder sb)
{ sb.Append(" world"); } // 修改內容,影響外部
public static void Replace(ref StringBuilder sb)
{ sb = new StringBuilder("new"); } // 替換參考,影響外部變數指向
// 呼叫端必須初始化
StringBuilder sb = new StringBuilder("hello");
Append(ref sb); // sb 變 "hello world"
Replace(ref sb); // sb 變 "new"
// TryParse 模式
public static bool TryGetUser(int id, out User user, out string error)
{
user = null; error = null; // 編譯器要求所有路徑指派
if (id <= 0) { error = "Invalid ID"; return false; }
user = _repo.Get(id);
return user != null;
}
// 解構語法 (C# 7+)
public static void Decompose(Point p, out int x, out int y)
{
x = p.X; y = p.Y;
}
// 呼叫
if (TryGetUser(1, out var user, out var err)) { ... }
Decompose(point, out int x, out int y); // 內嵌宣告
// ✅ 合法多載:參數類型不同
void M(int i) { }
void M(ref int i) { }
void M(out int i) { }
void M(params int[] arr) { }
// ❌ 非法多載:僅 ref/out 差異
void M(ref int i) { }
void M(out int i) { } // CS0663: 無法定義僅 ref/out 不同的多載
// ⚠️ 呼叫時歧義性
M(1); // 呼叫 M(int)
M(ref x); // 呼叫 M(ref int)
M(out y); // 呼叫 M(out int)
值傳遞 (預設): 實值型別 → 複製值 (棧上獨立副本) 參考型別 → 複製參考 (指向同一堆積物件) ref 參考傳遞: 實值型別 → 傳遞變數位址 (方法操作同一棧位置) 參考型別 → 傳遞參考變數位址 (可改變外部變數指向) out 輸出參數: 語意上等同 ref,但編譯器強制: 1. 呼叫端不需初始化 2. 方法內所有執行路徑必須指派 3. 方法內讀取前必須先指派 (視為未初始化)
// ref return:回傳參考而非複製值
public ref int FindMax(ref int[] numbers)
{
int maxIndex = 0;
for (int i = 1; i < numbers.Length; i++)
if (numbers[i] > numbers[maxIndex]) maxIndex = i;
return ref numbers[maxIndex]; // 回傳陣列元素的參考
}
// 使用
int[] arr = { 1, 5, 3 };
ref int max = ref FindMax(ref arr);
max = 10; // 直接修改陣列元素
// arr 變 { 1, 10, 3 }
// in 修飾詞:唯讀參考傳遞 (避免防禦性複製)
public readonly struct BigStruct { public int X, Y, Z; }
public static void Process(in BigStruct data) // 不允許修改 data
{
// data.X = 1; // 編譯錯誤
Console.WriteLine(data.X);
}
// ref struct:僅能存在於棧上 (高效能切片)
public ref struct Tokenizer
{
private ReadOnlySpan<char> _input;
public Tokenizer(ReadOnlySpan<char> input) => _input = input;
}
ref/out/params 是 C# 參數傳遞機制的三大擴展。核心差異在於初始化要求、指派義務、參考方向。現代 C# (7.0+) 引入 ref return、in、ref struct 進一步豐富了參考語意,配合 Span<T> 實現高效能無配置操作。實務中以不可變型別與函數式回傳取代 ref/out,代碼更安全、可測試、易平行化。