
1. 一维数组参数传递基础在C#中数组是引用类型这意味着当我们将数组作为参数传递给方法时实际上传递的是对数组的引用而非数组本身的副本。这种特性使得数组参数传递在内存使用和性能方面非常高效。1.1 基本语法结构传递一维数组作为方法参数的基本语法如下// 方法声明 void ProcessArray(int[] array) { // 方法体 } // 调用方法 int[] myArray {1, 2, 3, 4, 5}; ProcessArray(myArray);这里的关键点在于方法参数类型必须与数组类型完全匹配传递的是数组引用不是数组副本方法内部对数组的修改会影响原始数组1.2 数组参数的内存模型理解数组参数传递的关键在于掌握其内存模型。当我们将数组传递给方法时实际上发生的是引用传递栈内存 myArray → 堆内存地址0x1234 堆内存(0x1234): [0] 1 [1] 2 [2] 3 [3] 4 [4] 5方法调用时栈上会创建一个新的引用变量指向同一个堆内存地址。这就是为什么方法内部对数组的修改会影响原始数组。2. 数组参数传递的四种典型场景2.1 只读访问模式当方法只需要读取数组内容而不需要修改时可以使用以下模式void PrintArray(int[] array) { foreach (var item in array) { Console.WriteLine(item); } }注意即使声明为只读访问C#编译器也无法阻止方法内部修改数组内容。如果需要真正的只读保证可以考虑使用IReadOnlyListT接口。2.2 修改数组内容方法可以直接修改传入数组的内容void IncrementAll(int[] numbers) { for (int i 0; i numbers.Length; i) { numbers[i]; } }这种模式常见于需要对数组进行原地(in-place)操作的场景如排序算法、数值计算等。2.3 返回新数组有时我们需要保持原始数组不变返回一个新数组int[] DoubleAll(int[] input) { int[] result new int[input.Length]; for (int i 0; i input.Length; i) { result[i] input[i] * 2; } return result; }这种模式在函数式编程中更为常见避免了副作用。2.4 数组作为输出参数数组也可以作为out参数传递void CreateArray(out int[] array, int size) { array new int[size]; for (int i 0; i size; i) { array[i] i * i; } } // 调用 CreateArray(out var squares, 10);3. 高级应用技巧3.1 数组参数与params关键字params关键字允许我们将可变数量的参数作为数组传递int Sum(params int[] numbers) { return numbers.Sum(); } // 可以这样调用 var total Sum(1, 2, 3, 4, 5);实用技巧params参数必须是方法的最后一个参数且一个方法只能有一个params参数。3.2 数组切片传递C# 8.0引入了数组切片功能可以传递数组的一部分void ProcessSlice(int[] array, Range range) { var slice array[range]; // 处理切片 } // 调用 int[] data {1, 2, 3, 4, 5}; ProcessSlice(data, 1..3); // 处理第2到第3个元素3.3 多维数组与交错数组虽然标题指定了一维数组但了解多维数组的传递方式也很重要// 二维数组 void ProcessMatrix(int[,] matrix) { // ... } // 交错数组(数组的数组) void ProcessJagged(int[][] jagged) { // ... }4. 性能优化与最佳实践4.1 避免不必要的数组分配频繁创建新数组会影响性能。考虑重用数组或使用ArrayPool// 使用ArrayPool减少GC压力 var pool ArrayPoolint.Shared; var array pool.Rent(1024); try { ProcessArray(array); } finally { pool.Return(array); }4.2 大数组传递的考虑对于非常大的数组需要考虑内存占用缓存局部性并行处理可能性4.3 安全性考虑当方法接收外部传入的数组时验证数组不为null验证数组长度符合预期考虑防御性拷贝关键数据void SafeProcess(int[] input) { if (input null) throw new ArgumentNullException(); if (input.Length 10) throw new ArgumentException(数组长度不足); // 必要时创建防御性副本 var copy (int[])input.Clone(); // ... }5. 实际应用案例5.1 数值计算示例void Normalize(double[] vector) { double sum vector.Sum(); for (int i 0; i vector.Length; i) { vector[i] / sum; } }5.2 字符串处理示例string[] FilterStrings(string[] input, Predicatestring predicate) { var list new Liststring(); foreach (var s in input) { if (predicate(s)) list.Add(s); } return list.ToArray(); }5.3 游戏开发中的应用void UpdateEnemyPositions(Enemy[] enemies, float deltaTime) { foreach (var enemy in enemies) { enemy.Position enemy.Velocity * deltaTime; } }6. 常见问题与解决方案6.1 数组为null的问题// 错误处理方式 void Process(int[] array) { if (array null) { throw new ArgumentNullException(nameof(array)); } // ... }6.2 数组长度不足问题void SafeProcess(int[] data) { if (data.Length 3) { throw new ArgumentException(数组至少需要3个元素); } // ... }6.3 多线程环境下的数组访问// 使用锁保护数组访问 object _lock new object(); int[] _sharedArray; void ThreadSafeAccess() { lock (_lock) { // 访问或修改_sharedArray } }7. 现代C#中的改进7.1 集合表达式(C# 12)// 更简洁的数组初始化 int[] array [1, 2, 3, 4, 5]; ProcessArray([1, 2, 3]); // 直接传递新数组7.2 Span 的使用void ProcessSpan(Spanint span) { // 可以处理数组、栈内存、非托管内存等 for (int i 0; i span.Length; i) { span[i] * 2; } } // 调用 int[] array new int[100]; ProcessSpan(array);Span 提供了更灵活的内存访问方式同时保持高性能。7.3 模式匹配增强void ProcessArray(object input) { if (input is int[] array) { // 安全处理数组 } }8. 性能对比测试下表比较了不同数组传递方式的性能特点传递方式内存开销修改影响原数组适用场景直接传递低是需要修改原数组克隆后传递高否需要保护原数组Span很低是高性能场景ArraySegment低是处理部分数组在实际项目中我通常遵循以下原则选择传递方式如果方法需要修改数组且调用方期望这种修改直接传递如果需要保护原数组不受修改传递克隆或使用不可变集合在性能关键路径考虑使用Span 或Memory