CSharp(十七) Array数组定义
一、Array 类概述
Array 是 C# 中所有数组的基类,位于 System 命名空间下。它提供了创建、操作、搜索和排序数组的静态方法和实例方法。在 C# 中,数组是引用类型,继承自 Array 类。
二、数组声明与创建
2.1 一维数组
// 声明并初始化
int[] numbers = new int[5];
int[] numbers2 = new int[] { 1, 2, 3, 4, 5 };
int[] numbers3 = { 1, 2, 3, 4, 5 }; // 简写形式
// 使用 Array 类静态方法创建
Array array = Array.CreateInstance(typeof(int), 5);
2.2 多维数组
// 二维数组
int[,] matrix = new int[3, 4];
int[,] matrix2 = { { 1, 2, 3 }, { 4, 5, 6 } };
// 三维数组
int[,,] cube = new int[2, 3, 4];
2.3 交错数组(数组的数组)
// 交错数组
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5 };
jagged[2] = new int[] { 6 };
三、Array 类常用属性
| 属性 | 说明 | 示例 |
|---|---|---|
Length |
获取数组所有维度元素的总数 | int len = arr.Length; |
LongLength |
获取 64 位长度的数组元素总数 | long len = arr.LongLength; |
Rank |
获取数组的维数 | int rank = arr.Rank; |
IsFixedSize |
数组是否具有固定大小(始终为 true) | bool fixed = arr.IsFixedSize; |
IsReadOnly |
数组是否只读(始终为 false) | bool ro = arr.IsReadOnly; |
int[] arr = { 1, 2, 3, 4, 5 };
Console.WriteLine(arr.Length); // 输出: 5
Console.WriteLine(arr.Rank); // 输出: 1 (一维数组)
int[,] matrix = new int[3, 4];
Console.WriteLine(matrix.Length); // 输出: 12 (3*4)
Console.WriteLine(matrix.Rank); // 输出: 2 (二维数组)
四、Array 类常用方法
4.1 数组遍历与访问
int[] arr = { 10, 20, 30, 40, 50 };
// 使用索引访问
Console.WriteLine(arr[0]); // 10
// 使用 GetValue / SetValue(适用于运行时类型不确定的情况)
Array array = arr;
Console.WriteLine(array.GetValue(0)); // 10
array.SetValue(100, 0); // 将索引0的值设为100
4.2 数组排序
int[] arr = { 5, 2, 8, 1, 9 };
// 升序排序
Array.Sort(arr);
// 结果: { 1, 2, 5, 8, 9 }
// 降序排序
Array.Sort(arr);
Array.Reverse(arr);
// 结果: { 9, 8, 5, 2, 1 }
// 部分排序
int[] arr2 = { 5, 2, 8, 1, 9, 3 };
Array.Sort(arr2, 1, 3); // 从索引1开始,排序3个元素
// 结果: { 5, 1, 2, 8, 9, 3 }
// 自定义排序(字符串按长度排序)
string[] names = { "Alice", "Bob", "Christina", "Dave" };
Array.Sort(names, (a, b) => a.Length.CompareTo(b.Length));
4.3 数组搜索
int[] arr = { 10, 20, 30, 40, 50 };
// 二分查找(要求数组已排序)
Array.Sort(arr);
int index = Array.BinarySearch(arr, 30); // 返回: 2
int notFound = Array.BinarySearch(arr, 25); // 返回负数,表示不存在
// 线性查找
int idx = Array.IndexOf(arr, 30); // 返回: 2
int lastIdx = Array.LastIndexOf(arr, 30); // 返回: 2
// 查找满足条件的元素
int[] numbers = { 1, 2, 3, 4, 5, 6 };
int firstEven = Array.Find(numbers, x => x % 2 == 0); // 返回: 2
int lastEven = Array.FindLast(numbers, x => x % 2 == 0); // 返回: 6
int[] allEven = Array.FindAll(numbers, x => x % 2 == 0); // 返回: { 2, 4, 6 }
int evenIndex = Array.FindIndex(numbers, x => x % 2 == 0); // 返回: 1
bool exists = Array.Exists(numbers, x => x > 5); // 返回: true
4.4 数组复制
int[] arr = { 1, 2, 3, 4, 5 };
// 浅拷贝(Clone)
int[] clone = (int[])arr.Clone();
// 使用 CopyTo
int[] target = new int[5];
arr.CopyTo(target, 0);
// 使用 Array.Copy
int[] dest = new int[5];
Array.Copy(arr, dest, arr.Length);
// 指定范围复制
int[] dest2 = new int[3];
Array.Copy(arr, 1, dest2, 0, 3); // 从arr[1]开始复制3个元素到dest2
// 结果: dest2 = { 2, 3, 4 }
// 使用 Array.ConstrainedCopy(保证原子性,失败时目标数组不受影响)
Array.ConstrainedCopy(arr, 0, dest, 0, arr.Length);
4.5 数组清空与填充
int[] arr = { 1, 2, 3, 4, 5 };
// 清空数组(设为默认值)
Array.Clear(arr, 0, arr.Length);
// 结果: { 0, 0, 0, 0, 0 }
// 部分清空
int[] arr2 = { 1, 2, 3, 4, 5 };
Array.Clear(arr2, 1, 3);
// 结果: { 1, 0, 0, 0, 5 }
// 填充数组
// 注意:Array 类没有 Fill 方法,但可以使用扩展或循环
// .NET Core 2.0+ 支持 Array.Fill
Array.Fill(arr, 9); // 全部填充为9
Array.Fill(arr, 7, 1, 3); // 从索引1开始,填充3个元素为7
4.6 数组反转
int[] arr = { 1, 2, 3, 4, 5 };
// 全部反转
Array.Reverse(arr);
// 结果: { 5, 4, 3, 2, 1 }
// 部分反转
int[] arr2 = { 1, 2, 3, 4, 5 };
Array.Reverse(arr2, 1, 3);
// 结果: { 1, 4, 3, 2, 5 }
4.7 数组遍历(ForEach)
string[] names = { "Alice", "Bob", "Charlie" };
// 使用 Array.ForEach
Array.ForEach(names, name => Console.WriteLine(name));
// 使用 foreach 语句(更常用)
foreach (var name in names)
{
Console.WriteLine(name);
}
五、多维数组操作
5.1 获取和设置多维数组元素
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };
// 获取维度长度
int rows = matrix.GetLength(0); // 2 (第一维长度)
int cols = matrix.GetLength(1); // 3 (第二维长度)
// 获取各维度下限(通常为0)
int lowerBound0 = matrix.GetLowerBound(0); // 0
int lowerBound1 = matrix.GetUpperBound(1); // 2
// 使用 GetValue / SetValue(参数为索引数组)
Array arr = matrix;
Console.WriteLine(arr.GetValue(0, 1)); // 2
arr.SetValue(100, 0, 1); // 将 matrix[0,1] 设为 100
5.2 遍历多维数组
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };
// 使用嵌套循环
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();
}
// 使用 foreach(按行优先顺序遍历)
foreach (int val in matrix)
{
Console.Write($"{val} ");
}
六、数组转换
// 数组转 List
int[] arr = { 1, 2, 3 };
List<int> list = arr.ToList();
// 数组转 IEnumerable
IEnumerable<int> enumerable = arr.AsEnumerable();
// 类型转换(Cast)
object[] objArr = { 1, 2, 3 };
int[] intArr = objArr.Cast<int>().ToArray();
// 使用 OfType 过滤特定类型
object[] mixed = { 1, "hello", 2, "world", 3 };
int[] ints = mixed.OfType<int>().ToArray(); // { 1, 2, 3 }
七、数组切片与范围(C# 8.0+)
int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// 使用范围运算符
int[] slice1 = arr[2..5]; // { 2, 3, 4 }
int[] slice2 = arr[..5]; // { 0, 1, 2, 3, 4 }
int[] slice3 = arr[5..]; // { 5, 6, 7, 8, 9 }
int[] slice4 = arr[^3..]; // { 7, 8, 9 } (最后3个)
int[] slice5 = arr[1..^1]; // { 1, 2, 3, 4, 5, 6, 7, 8 } (去掉首尾)
// 使用 Span(高效内存切片,不复制数据)
Span<int> span = arr.AsSpan(2, 5); // 引用 arr[2..7]
八、数组与 LINQ
int[] numbers = { 5, 2, 8, 1, 9, 3 };
// 排序
var sorted = numbers.OrderBy(n => n).ToArray();
// 过滤
var evens = numbers.Where(n => n % 2 == 0).ToArray();
// 投影
var squares = numbers.Select(n => n * n).ToArray();
// 聚合
int sum = numbers.Sum();
int max = numbers.Max();
int min = numbers.Min();
double avg = numbers.Average();
// 分组
var groups = numbers.GroupBy(n => n % 2).ToArray();
九、性能优化技巧
9.1 使用 Span 和 Memory
int[] arr = { 1, 2, 3, 4, 5 };
// Span - 栈上分配的内存切片(高性能,无堆分配)
Span<int> span = arr.AsSpan(1, 3);
span[0] = 100; // 修改会影响原数组
// ReadOnlySpan - 只读切片
ReadOnlySpan<int> roSpan = arr.AsSpan();
// Memory - 可用于异步操作的内存切片
Memory<int> memory = arr.AsMemory(1, 3);
9.2 数组池(ArrayPool)
// 使用 ArrayPool 减少 GC 压力
int[] rented = ArrayPool<int>.Shared.Rent(100);
try
{
// 使用 rented 数组(长度可能大于100)
for (int i = 0; i < 100; i++)
{
rented[i] = i;
}
}
finally
{
// 必须归还到池中
ArrayPool<int>.Shared.Return(rented);
}
十、完整示例代码
using System;
using System.Linq;
class Program
{
static void Main()
{
// 1. 创建数组
int[] numbers = { 5, 2, 8, 1, 9, 3 };
Console.WriteLine($"原数组: {string.Join(", ", numbers)}");
// 2. 排序
Array.Sort(numbers);
Console.WriteLine($"排序后: {string.Join(", ", numbers)}");
// 3. 反转
Array.Reverse(numbers);
Console.WriteLine($"反转后: {string.Join(", ", numbers)}");
// 4. 查找
int index = Array.IndexOf(numbers, 5);
Console.WriteLine($"5 的索引: {index}");
// 5. 二分查找
Array.Sort(numbers);
int found = Array.BinarySearch(numbers, 5);
Console.WriteLine($"二分查找 5: {found}");
// 6. 复制
int[] copy = new int[numbers.Length];
Array.Copy(numbers, copy, numbers.Length);
Console.WriteLine($"复制: {string.Join(", ", copy)}");
// 7. 使用 LINQ
var evens = numbers.Where(n => n % 2 == 0).ToArray();
Console.WriteLine($"偶数: {string.Join(", ", evens)}");
// 8. 多维数组
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };
Console.WriteLine($"矩阵行数: {matrix.GetLength(0)}, 列数: {matrix.GetLength(1)}");
// 9. 遍历多维数组
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();
}
}
}
十一、注意事项
- 数组越界:访问
arr[arr.Length]会抛出IndexOutOfRangeException - 数组是固定大小:创建后不能改变大小,需要调整大小请使用
Array.Resize或List<T> - 引用类型数组:数组元素是引用类型时,存储的是引用而非对象本身
- 协变数组:C# 支持数组协变,但可能导致运行时异常,建议使用泛型集合
// 数组协变(需谨慎使用)
object[] objArr = new string[3];
objArr[0] = "hello"; // 正常
// objArr[1] = 123; // 运行时抛出 ArrayTypeMismatchException