陣列 是固定大小、同質元素的連續記憶體結構,是 .NET 最基礎的集合型別。陣列實作 IList<T>、IReadOnlyList<T>、IEnumerable<T>,提供隨機存取 O(1)、極佳快取局部性。本文涵蓋宣告、初始化、多維/交錯陣列、Array 類別方法、Span<T> 高效能切片。
// 宣告
int[] numbers; // 未初始化,值為 null
int[] numbers2 = new int[5]; // 預設值 0
int[] numbers3 = new int[] {1,2,3,4,5}; // 隱含大小
int[] numbers4 = {1,2,3,4,5}; // 簡寫 (僅宣告時)
var numbers5 = new[] {1,2,3}; // 型別推斷
// 索引存取 (0-based)
numbers2[0] = 10;
var first = numbers2[0];
// 陣列為參考型別
int[] a = {1,2,3};
int[] b = a; // 同一參考
b[0] = 99;
Console.WriteLine(a[0]); // 99
// 複製陣列
int[] c = (int[])a.Clone(); // 淺層複製
int[] d = a.ToArray(); // LINQ 擴充方法
int[] e = new int[a.Length];
Array.Copy(a, e, a.Length); // 低層複製
a.CopyTo(e, 0); // 實例方法
所有維度大小固定,記憶體連續:
// 宣告: 2 維度,3列 4行
int[,] matrix = new int[3, 4];
int[,] matrix2 = { {1,2,3}, {4,5,6}, {7,8,9} }; // 3x3
// 存取
matrix[1, 2] = 42; // 第 1 列、第 2 行
// 迭代
for (int i = 0; i < matrix.GetLength(0); i++) {
for (int j = 0; j < matrix.GetLength(1); j++) {
Console.Write($"{matrix[i,j]} ");
}
Console.WriteLine();
}
// 屬性
matrix.Rank; // 2 (維度數)
matrix.Length; // 12 (總元素數)
matrix.GetLength(0); // 3 (第 0 維大小)
陣列的陣列,每列可不同長度,記憶體不連續:
// 宣告: 3 列,每列長度不同
int[][] jagged = new int[3][];
jagged[0] = new int[] {1, 2};
jagged[1] = new int[] {3, 4, 5, 6};
jagged[2] = new int[] {7};
// 初始化簡寫
int[][] jagged2 = {
new[] {1, 2},
new[] {3, 4, 5},
new[] {6}
};
// 存取
jagged[1][2] = 99; // 第 1 列、第 2 元素
// 迭代
foreach (var row in jagged) {
foreach (var val in row) Console.Write($"{val} ");
Console.WriteLine();
}
選擇指南:矩形陣列適合矩陣運算、影像處理;交錯陣列適合不規則資料(如稀疏矩陣、不定長序列)。
| 方法 | 說明 |
|---|---|
Sort(Array) | 原地排序 (QuickSort/IntroSort),支援自訂 IComparer |
BinarySearch | 二分搜尋,陣列必須已排序 |
Reverse | 原地反轉 |
Copy | 高效複製區段,支援跨型別 (需相容) |
ConstrainedCopy | 不可部分完成複製 (全成功或全失敗) |
Resize | 調整大小 (建立新陣列複製) |
Clear | 將區段設為預設值 |
Find/FindAll/FindIndex | 搜尋符合條件元素 |
ConvertAll | 型別轉換投影 |
ForEach | 對每個元素執行 Action |
AsReadOnly | 建立唯讀包裝 |
陣列隱含實作:
IList<T>:Count、this[int]、Add/Remove 擲出 NotSupportedExceptionIReadOnlyList<T>:唯讀存取IEnumerable<T>:foreach、LINQint[] arr = {3,1,4,1,5};
IList list = arr;
list.Count; // 5
list[2]; // 4
// list.Add(9); // 執行期例外
// LINQ 直接使用
var evens = arr.Where(x => x % 2 == 0).ToArray(); // {4}
.NET Core 2.1+ Span<T> 是 ref struct,提供陣列區段的安全、零配置、堆疊專用視圖:
int[] data = {1,2,3,4,5,6,7,8,9,10};
Span slice = data.AsSpan(2, 4); // 索引 2 起長度 4: {3,4,5,6}
// 修改切片會影響原陣列
slice[0] = 99; // data[2] 變為 99
// 切片操作
var first3 = slice.Slice(0, 3); // {99,4,5}
var rest = slice[3..]; // Range 索引 (C# 8+)
// 避免子陣列配置
void Process(Span buffer) { /* ... */ }
Process(data.AsSpan(100, 50)); // 零配置
限制:Span<T> 不可作為類別欄位、陣列元素、封箱、跨 await/yield。長壽命切片用 Memory<T>。
ArraySegment<T> 是結構體,包裝陣列+偏移+長度,適用舊版 API:
var segment = new ArraySegment(data, 2, 4);
foreach (var x in segment) Console.WriteLine(x);
matrix[i,j] 有邊界檢查開銷;熱路徑考慮交錯陣列或一維陣列手動索引 i * width + j。System.Numerics.Vector<T> SIMD、ArrayPool<T> 重用緩衝區。IReadOnlyList<T> 或 ReadOnlySpan<T>,避免呼叫端修改內部陣列。ArrayPool<T>.Shared.Rent(size) 避免 LOH 配置/GC。A: 不能。Array.Resize 是建立新陣列並複製,原參考不變。需動態大小請用 List<T>。
A: 交錯陣列通常較快:單一索引 arr[i][j] 編譯為兩次簡單索引;多維陣列 arr[i,j] 有方法呼叫與邊界檢查開銷。但多維陣列記憶體連續,大型矩陣快取表現較好。
A: 回傳 array.ToArray() 複本、或 array.AsReadOnly()、或 ReadOnlySpan<T>。