C# 參數傳遞機制常讓開發者混淆:「傳遞參考型別」≠「ref 傳遞」。陣列為參考型別,預設以值傳遞參考(即複製陣列參考),導致被呼叫端能修改陣列元素內容,卻無法替換整個陣列實例。本文深入剖析此行為,並提供三層防禦策略:readonly、唯讀介面、防禦性複製。
| 分類 | 傳遞機制 | 能否修改元素 | 能否替換整個陣列 |
|---|---|---|---|
| 陣列(參考型別) 預設(值傳參考) | 複製參考 | 是 | 否(只影響區域變數) |
陣列 + ref | 傳遞參考的參考 | 是 | 是 |
List<T> 預設 | 複製參考 | 是 | 否 |
| 結構體(值型別)預設 | 複製實例 | 否(修改副本) | 不適用 |
void ModifyArray(int[] arr)
{
arr[0] = 999; // 影響原陣列元素
arr = new int[] { 1, 2 }; // 僅改變區域參考,呼叫端不受影響
}
void ReplaceArray(ref int[] arr)
{
arr = new int[] { 1, 2 }; // 呼叫端陣列變數指向新實例
}
int[] a = { 10, 20, 30 };
ModifyArray(a);
Console.WriteLine(a[0]); // 999 ← 元素被改了
ReplaceArray(ref a);
Console.WriteLine(a[0]); // 1 ← 整個陣列被換了
宣告參數為 IReadOnlyList<T> 或 IReadOnlyCollection<T>,編譯期即阻擋修改嘗試:
// 呼叫端
int[] data = { 1, 2, 3 };
Process(data); // 隱含轉換 int[] → IReadOnlyList
// 被呼叫端
void Process(IReadOnlyList items)
{
// items[0] = 9; // CS0200: 唯讀索引子不可指派
foreach (var x in items) Console.WriteLine(x); // 唯讀遍歷安全
}
優點:零額外記憶體、零複製、意圖明確、支援陣列與 List<T> 多型。
缺點:無法阻止惡意轉型 (items as int[])?[0] = 9,但屬「自找麻煩」層級。
若必須確保內部邏輯不受外部後續修改影響,或需回傳不可變快照:
void Process(int[] input)
{
int[] snapshot = (int[])input.Clone(); // 淺層複製,值型別元素安全
// 對 snapshot 操作,不影響原陣列
}
int[] GetData()
{
return _internalArray.ToArray(); // 回傳副本,保護內部狀態
}
陣列為一維且元素為值型別時,Clone()/ToArray() 即為深層複製;元素為參考型別時,僅複製參考,仍需考慮元素不可變性。
.NET 6+ / NuGet System.Collections.Immutable:
using System.Collections.Immutable;
void Process(ImmutableArray items) // 結構體,傳值語意,絕對不可變
{
// items[0] = 9; // 編譯錯誤
var newItems = items.Add(4); // 產生新實例,原不變
}
ImmutableArray arr = ImmutableArray.Create(1, 2, 3);
Process(arr);
IReadOnlyList<T> > IEnumerable<T> > T[] / List<T>。ReadOnlySpan<T> (.NET Core 2.1+),零配置、棧上、高效能。private T[] _arr,提供 IReadOnlyList<T> Items => _arr 或 _arr.ToArray() 副本。ref/out 陣列:除非極少數高效能互換場景,否則破壞封裝、增加認知負擔。readonly 修飾陣列欄位就安全了」:readonly int[] _arr 只防止替換陣列實例,_arr[0] = 9 依然合法。params 陣列就不會被改」:params 只是語法糖,本質仍是陣列參考傳遞。Span<T> 就能防禦」:Span<T> 本身可修改元素,需用 ReadOnlySpan<T>。掌握「參考型別值傳遞 = 複製參考」這一核心規則,配合 IReadOnlyList<T> / ReadOnlySpan<T> / ImmutableArray<T> 現代工具箱,即能在編譯期與執行期雙重保障陣列參數的安全傳遞,消除難以重現的「莫名其妙資料被改」Bug。