C#数组深度解析:从基础语法到实战应用

一、引言:数组 ------C# 数据存储的基石

在 C# 编程的广袤世界里,数组堪称数据存储与处理的基石。它就像一个整齐排列的集装箱,将大量相同类型的数据有序封装,让我们能够通过索引快速定位和操作这些元素,大大提高了数据处理的效率。无论是开发小型控制台应用,还是构建大型企业级系统,数组都扮演着不可或缺的角色。


二、C# 数组基础概念与核心特性

(一)数组的本质与核心特性

数组是相同数据类型元素的有序集合,在内存中以连续块形式存储,通过索引(从 0 开始)实现 O (1) 时间复杂度的元素访问。核心特性包括:

  1. 固定长度 :创建时确定大小,后续不可改变(区别于动态集合如 List) 。例如,当我们创建一个包含 5 个整数的数组int[] numbers = new int[5]; ,其长度就固定为 5,无法在不重新创建数组的情况下改变其大小。

  2. 强类型约束 :元素类型必须统一,确保编译期类型安全 。如果定义一个int类型的数组int[] ages = {18, 20, 22};,就不能将字符串或其他类型的数据放入其中,否则会在编译时报错。

  3. 引用类型本质 :数组变量存储内存地址,实际数据存储在托管堆 。即使数组中存储的是值类型(如int),数组本身在 C# 中仍是引用类型。这意味着当将一个数组变量赋值给另一个数组变量时,实际上是复制了内存地址,而不是数据本身。例如:

csharp 复制代码
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1;
arr2[0] = 99;
Console.WriteLine(arr1[0]); // 输出99,因为arr1和arr2指向同一内存地址

(二)数组的分类与内存结构

  1. 一维数组 :最常用形式,如int[] scores = {85, 92, 78} 。它是最简单的数组类型,元素按线性顺序排列,只需一个索引即可访问元素,在内存中占据一段连续的空间。例如:
csharp 复制代码
int[] numbers = new int[3]; // 声明长度为3的一维数组,元素默认初始化为0
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
  1. 多维数组(矩形数组) :行列固定的二维 / 三维数组,如int[,] matrix = new int[3,4] 。以二维数组为例,它可以看作是一个表格,有固定的行数和列数,需要两个索引(行索引和列索引)来访问元素。内存中按行优先顺序连续存储。例如:
csharp 复制代码
int[,] matrix = new int[2, 3]; // 声明一个2行3列的二维数组
matrix[0, 0] = 1;
matrix[0, 1] = 2;
matrix[0, 2] = 3;
matrix[1, 0] = 4;
matrix[1, 1] = 5;
matrix[1, 2] = 6;
  1. 交错数组(Jagged Array) :数组的数组,各行长度可不同,如int[][] jaggedArray = new int[3][] 。交错数组本质上是一维数组,其每个元素又是一个独立的数组,这些子数组的长度可以灵活设置,在内存中不连续存储。例如:
csharp 复制代码
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2 };
jaggedArray[1] = new int[] { 3, 4, 5 };
jaggedArray[2] = new int[] { 6 };

三、数组的声明与初始化全攻略

(一)一维数组初始化方式

  1. 显式指定长度初始化 :使用new关键字创建数组时指定长度,元素会被初始化为类型的默认值 。例如,int类型数组的元素默认值为 0,string类型数组的元素默认值为null。如:
csharp 复制代码
int[] numbers = new int[5];  // 创建长度为5的数组,元素默认值为0
  1. 值列表初始化 :在创建数组时直接提供元素值列表,编译器会根据列表元素个数确定数组长度 。完整语法需new关键字,也有省略new关键字的简化写法。例如:
csharp 复制代码
string[] names = new string[] { "Alice", "Bob", "Charlie" };
// 简化语法:省略new关键字
string[] names = { "Alice", "Bob", "Charlie" };
  1. 延迟初始化:先声明数组变量,之后再进行初始化,适用于在程序执行过程中根据条件确定数组内容的场景 。例如:
csharp 复制代码
int[] data;
data = new int[] { 1, 2, 3 };  // 声明与初始化分离

(二)多维数组与交错数组初始化

  1. 二维数组初始化:二维数组(矩形数组)有静态初始化和动态创建两种方式 。静态初始化时直接在大括号内提供所有元素值,按行排列;动态创建则先指定行数和列数,后续再逐个赋值。例如:
csharp 复制代码
// 静态初始化,直接指定所有元素值
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
// 动态创建,先指定行数和列数,后续再逐个赋值
int[,] matrix = new int[3, 3];
matrix[0, 0] = 1;
matrix[0, 1] = 2;
// 依此类推为其他元素赋值
  1. 交错数组初始化:交错数组(数组的数组)的初始化相对复杂,需要先创建外层数组,再为每个内层数组单独分配内存并赋值 。例如:
csharp 复制代码
int[][] jagged = new int[3][];  // 创建包含3个数组的外层数组
jagged[0] = new int[] { 1, 2 };  // 为第一个内层数组赋值
jagged[1] = new int[] { 3, 4, 5 };  // 为第二个内层数组赋值
jagged[2] = new int[] { 6 };  // 为第三个内层数组赋值

(三)特殊场景初始化技巧

  1. 默认值初始化 :当使用指定长度的方式初始化数组时,数组元素会自动初始化为其类型的默认值 。数值类型(如intdouble)默认为 0,引用类型(如string、自定义类)默认为null,布尔类型默认为false。例如:
csharp 复制代码
double[] prices = new double[3];  // 数组元素默认值为0.0
string[] cities = new string[2];  // 数组元素默认值为null
bool[] flags = new bool[4];  // 数组元素默认值为false
  1. 使用 Array.CreateInstance 动态创建Array.CreateInstance方法可在运行时动态创建数组,适用于在编译期无法确定数组类型和维度的复杂场景 。通过反射获取类型信息并创建相应数组。例如:
csharp 复制代码
// 创建一个包含5个int类型元素的数组
Array array = Array.CreateInstance(typeof(int), 5);
array.SetValue(10, 0);  // 设置第一个元素的值为10
int value = (int)array.GetValue(0);  // 获取第一个元素的值

四、数组常用操作与高效处理技巧

(一)基础操作:访问、修改与遍历

  1. 元素访问与修改 :在 C# 中,通过索引可以轻松访问和修改数组元素 。索引从 0 开始,如scores[0] = 90; ,将scores数组的第一个元素赋值为 90;int score = scores[2]; 则获取scores数组的第三个元素的值。在实际应用中,例如统计学生成绩时,我们可以通过索引快速定位某个学生的成绩并进行修改。比如,在一个存储学生数学成绩的数组int[] mathScores = {88, 92, 76, 85}; 中,如果要将第三个学生的成绩从 76 改为 80,可以这样操作:mathScores[2] = 80;

  2. 遍历方式对比

    • for 循环:是最常见的遍历方式,通过索引控制循环,适用于需要对元素进行读写操作的场景 。例如,计算学生成绩总和:
csharp 复制代码
int[] scores = {85, 92, 78, 88};
int sum = 0;
for (int i = 0; i < scores.Length; i++)
{
    sum += scores[i];
}
  • foreach 循环:语法简洁,直接访问元素值,无需手动管理索引,常用于只读遍历 。例如,输出学生成绩:
csharp 复制代码
int[] scores = {85, 92, 78, 88};
foreach (var score in scores)
{
    Console.WriteLine(score);
}
  • Array.ForEach 方法:使用委托对数组每个元素执行操作,代码简洁,适用于简单的遍历处理 。例如,输出学生成绩:
csharp 复制代码
int[] scores = {85, 92, 78, 88};
Array.ForEach(scores, score => Console.WriteLine(score));

(二)高级操作:排序、查找与复制

  1. 排序与反转 :C# 提供了强大的数组排序和反转方法 。Array.Sort方法可对数组进行升序排序,数组元素需实现IComparable接口;Array.Reverse方法则用于反转数组顺序。例如,对学生成绩数组进行排序和反转:
csharp 复制代码
int[] scores = {85, 92, 78, 88};
Array.Sort(scores);  // 升序排序,结果为{78, 85, 88, 92}
Array.Reverse(scores);  // 反转数组,结果为{92, 88, 85, 78}
  1. 查找与索引 :在实际开发中,查找数组中的元素是常见需求 。Array.IndexOf方法用于查找元素首次出现的索引;Array.Exists方法可根据条件判断数组中是否存在满足条件的元素。例如,查找成绩为 90 的学生索引,以及判断是否有成绩大于 90 的学生:
csharp 复制代码
int[] scores = {85, 92, 78, 88};
int index = Array.IndexOf(scores, 90);  // 查找90的索引,若不存在返回 -1
bool exists = Array.Exists(scores, x => x > 90);  // 判断是否有成绩大于90的学生
  1. 数组复制与克隆 :当需要复制数组时,Array.Copy方法提供了高效的内存复制方式,需指定源数组、目标数组和复制长度;Clone方法则用于创建数组的浅拷贝,对于值类型数组是安全的,对于引用类型数组仅复制引用 。例如:
csharp 复制代码
int[] sourceArray = {1, 2, 3, 4, 5};
int[] targetArray = new int[5];
Array.Copy(sourceArray, targetArray, sourceArray.Length);  // 复制数组
int[] cloneArray = (int[])sourceArray.Clone();  // 克隆数组

(三)内存管理与性能优化

  1. 避免频繁扩容:数组在初始化时确定大小,若频繁扩容(如使用动态数组时超出初始容量),会导致内存重新分配和数据复制,严重影响性能 。因此,在创建数组时,应根据实际需求合理预估大小,避免频繁扩容。比如,在处理大量学生成绩时,如果已知学生数量为 1000 人,那么在创建成绩数组时就应直接指定长度为 1000,而不是先创建一个较小的数组,然后在添加成绩时不断扩容。

  2. 使用 Span 和 MemorySpan<T>Memory<T>是 C# 7.2 及以上版本引入的新类型,用于高效处理内存中的连续数据块,避免不必要的复制 。Span<T>适用于栈上分配的内存,Memory<T>适用于堆上分配的内存,常用于高性能场景,如数据解析、加密算法等。例如,在处理字节数组时:

csharp 复制代码
byte[] data = new byte[1024];
// 使用Span
Span<byte> span = data;
for (int i = 0; i < span.Length; i++)
{
    span[i] = (byte)i;
}
// 使用Memory
Memory<byte> memory = data;
Memory<byte> subMemory = memory.Slice(0, 100);

五、多维数组与交错数组深度解析

(一)多维数组:规则数据的矩阵化存储

在 C# 中,多维数组是一种将数据以矩阵形式存储的结构,其中最常见的是二维数组,它可以看作是一个表格,有固定的行数和列数。例如,在数学运算中,我们经常需要处理矩阵,二维数组就可以很好地表示矩阵。

csharp 复制代码
int[,] matrix = new int[3, 4];

上述代码创建了一个 3 行 4 列的二维数组,共包含 12 个元素。

内存布局

多维数组在内存中按行优先顺序连续存储。这意味着,对于一个二维数组matrix[i,j],其在一维内存中的索引可以通过公式i * columns + j计算得出 。例如,对于一个3x4的二维数组,matrix[1,2]的一维索引为1 * 4 + 2 = 6 。这种连续存储方式使得多维数组在遍历和进行矩阵运算时具有较高的性能。

维度操作

在处理多维数组时,我们常常需要获取数组的维度信息 。通过GetLength方法可以获取指定维度的长度。例如:

csharp 复制代码
int rows = matrix.GetLength(0);  // 获取第一维长度,即行数
int columns = matrix.GetLength(1);  // 获取第二维长度,即列数
适用场景
  1. 数学矩阵运算:如矩阵乘法、矩阵转置等操作,二维数组的结构与数学矩阵一一对应,方便实现算法 。例如,实现两个矩阵相乘的代码如下:
csharp 复制代码
int[,] matrixA = { { 1, 2 }, { 3, 4 } };
int[,] matrixB = { { 5, 6 }, { 7, 8 } };
int rowsA = matrixA.GetLength(0);
int colsA = matrixA.GetLength(1);
int colsB = matrixB.GetLength(1);
int[,] result = new int[rowsA, colsB];

for (int i = 0; i < rowsA; i++)
{
    for (int j = 0; j < colsB; j++)
    {
        for (int k = 0; k < colsA; k++)
        {
            result[i, j] += matrixA[i, k] * matrixB[k, j];
        }
    }
}
  1. 表格数据存储:数据库查询结果、Excel 表格数据等规则的二维表格数据,可以直接用二维数组存储和处理 。例如,从数据库中查询学生成绩表,将成绩存储在二维数组中:
csharp 复制代码
int[,] scores = { { 85, 90, 78 }, { 92, 88, 95 }, { 76, 82, 80 } };
  1. 游戏地图等规则网格数据:在游戏开发中,二维数组常用于表示游戏地图的地形、角色位置等信息 。例如,用二维数组表示一个简单的游戏地图,0 表示空地,1 表示障碍物:
csharp 复制代码
int[,] gameMap = { { 0, 0, 0, 1 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 } };

(二)交错数组:不规则数据的灵活组织

交错数组,也称为数组的数组,是一种更为灵活的数据结构,其每行是独立的一维数组,因此各行长度可以不同 。例如,当我们存储不同长度的文本段落时,交错数组就非常适用。

csharp 复制代码
string[][] paragraphs = new string[3][];
paragraphs[0] = new string[] { "This", "is", "the", "first", "paragraph" };
paragraphs[1] = new string[] { "Second", "paragraph", "is", "shorter" };
paragraphs[2] = new string[] { "Last", "one" };
结构特点

交错数组本质上是一维数组,其每个元素又是一个独立的一维数组 。这种结构使得交错数组在存储不规则数据时具有很高的灵活性,但由于每行数组在内存中不连续存储,所以在某些操作上性能可能不如多维数组。

遍历方式

遍历交错数组通常使用双重循环,外层循环遍历行,内层循环遍历每行中的元素 。例如:

csharp 复制代码
for (int i = 0; i < paragraphs.Length; i++)
{
    for (int j = 0; j < paragraphs[i].Length; j++)
    {
        Console.Write(paragraphs[i][j] + " ");
    }
    Console.WriteLine();
}
应用场景
  1. 存储长度不一的数据集:如数据库查询结果中,不同行的字段数量可能不同,使用交错数组可以方便地存储这些数据 。例如,从数据库中查询不同长度的用户信息:
csharp 复制代码
object[][] userInfo = new object[3][];
userInfo[0] = new object[] { 1, "Alice", 25 };
userInfo[1] = new object[] { 2, "Bob" };
userInfo[2] = new object[] { 3, "Charlie", "Male", "Engineer" };
  1. 动态报表行数据:在生成动态报表时,每一行的数据列数可能不同,交错数组可以灵活地存储这些行数据 。例如,生成一个销售报表,不同产品的销售数据列数不同:
csharp 复制代码
decimal[][] salesData = new decimal[3][];
salesData[0] = new decimal[] { 100.5m, 200.3m };
salesData[1] = new decimal[] { 50.2m, 75.8m, 120.0m };
salesData[2] = new decimal[] { 300.1m };

六、数组与常见集合类型的对比与选型指南

在 C# 编程中,数组作为基础的数据存储结构,与其他集合类型(如 List、ImmutableArray 等)各有优劣,在不同场景下发挥着不同的作用。了解它们之间的差异,有助于我们在开发中做出更合适的选择,提升程序的性能和可维护性。

(一)数组 vs List:固定大小 vs 动态扩容

数组和 List 是 C# 中常用的数据存储结构,它们在大小可变、类型安全、内存效率等方面存在明显差异,适用于不同的应用场景。具体对比如下:

特性 数组 List
大小可变 否(创建后固定) 是(自动扩容)
类型安全 是(编译期检查) 是(泛型支持)
内存效率 更高(连续存储无额外开销) 稍低(包含扩容机制开销)
适用场景 已知固定大小的数据存储 动态增长的列表数据

(二)数组 vs ImmutableArray:可变 vs 不可变

  1. 数组:支持原地修改,适合需要频繁更新元素的场景 。例如,在一个实时监控系统中,需要不断更新传感器采集的数据,使用数组可以直接在原数组上进行修改,操作简单高效。

  2. ImmutableArray:不可变,每次修改返回新实例,适合线程安全的只读场景 。比如,在多线程环境下,一个配置文件的内容在程序运行期间不会改变,使用 ImmutableArray 可以确保数据的一致性和线程安全性,避免多线程同时修改数据导致的错误。

(三)选型决策树

在实际开发中,如何选择合适的数据结构是一个关键问题。下面是一个简单的选型决策树,帮助我们根据具体需求做出选择:

  1. 数据大小是否固定?

    • :优先使用数组(性能最优) 。例如,在一个计算学生成绩平均分的程序中,已知学生数量固定,使用数组存储成绩可以提高内存利用率和访问效率。

    • :考虑 List(动态扩容)或其他集合(如 Queue、Stack) 。比如,在一个任务管理系统中,任务数量可能会动态增加,使用 List 可以方便地添加和管理任务。

  2. 是否需要键值对存储?

    • :Dictionary<TKey,TValue>(哈希表实现,快速查找) 。例如,在一个用户信息管理系统中,需要根据用户 ID 快速查找用户信息,使用 Dictionary 可以实现高效的查找操作。
  3. 是否要求线程安全且只读?

    • :ImmutableArray(无锁访问) 。比如,在一个多线程的日志记录系统中,日志数据在记录后不需要修改,使用 ImmutableArray 可以保证线程安全,同时提高读取性能。

七、实战案例:数组在典型场景中的应用

(一)数据统计与分析:计算学生成绩的平均分、最高分

在教育管理系统中,我们经常需要对学生成绩进行统计分析。假设我们有一个存储学生成绩的数组,通过简单的数组操作,就可以轻松计算出平均分和最高分。

csharp 复制代码
int[] scores = { 85, 92, 78, 88, 95 };
int sum = 0;
int maxScore = int.MinValue;
foreach (int score in scores)
{
    sum += score;
    if (score > maxScore)
    {
        maxScore = score;
    }
}
double average = (double)sum / scores.Length;
Console.WriteLine($"平均分: {average}, 最高分: {maxScore}");

(二)矩阵运算与科学计算:实现二维矩阵乘法

在科学计算领域,矩阵运算是常见的操作。下面的代码展示了如何使用二维数组实现矩阵乘法。

csharp 复制代码
int[,] matrixA = { { 1, 2 }, { 3, 4 } };
int[,] matrixB = { { 5, 6 }, { 7, 8 } };
int rowsA = matrixA.GetLength(0);
int colsA = matrixA.GetLength(1);
int colsB = matrixB.GetLength(1);
int[,] result = new int[rowsA, colsB];
for (int i = 0; i < rowsA; i++)
{
    for (int j = 0; j < colsB; j++)
    {
        for (int k = 0; k < colsA; k++)
        {
            result[i, j] += matrixA[i, k] * matrixB[k, j];
        }
    }
}

(三)文件读写与二进制处理:读取文件内容到字节数组并处理

在文件处理中,我们常常需要将文件内容读取到字节数组中进行处理。以下是使用File.ReadAllBytes方法读取文件内容的示例。

csharp 复制代码
string filePath = "example.txt";
byte[] fileBytes = File.ReadAllBytes(filePath);
// 对字节数组进行处理,例如解密、解析等

八、最佳实践与常见陷阱规避

(一)性能优化建议

  1. 避免过度使用装箱拆箱 :装箱是将值类型转换为引用类型,拆箱则是反向操作,这两个过程都会带来额外的性能开销。例如,当将一个int类型的值放入ArrayList集合中时,就会发生装箱操作;从ArrayList中取出值时,又会发生拆箱操作。在 C# 中,应优先使用泛型数组(如int[]而非object[]),因为泛型集合直接存储值类型,不会产生装箱拆箱,能极大提高代码运行效率。比如:
csharp 复制代码
// 不推荐,会发生装箱操作
ArrayList list = new ArrayList();
list.Add(10);  // 装箱
int value = (int)list[0];  // 拆箱

// 推荐,使用泛型集合避免装箱拆箱
List<int> genericList = new List<int>();
genericList.Add(10);
int value = genericList[0];
  1. 合理设置初始容量 :对已知大小的数据,初始化时指定长度避免多次扩容。以List<T>为例,当添加元素超过初始容量时,List<T>会重新分配内存,将原数据复制到新的内存位置,这一过程会消耗大量时间和资源。因此,在创建List<T>时,如果能预先知道大致的数据量,应指定初始容量。例如:
csharp 复制代码
// 推荐,指定初始容量
List<int> numbers = new List<int>(100);  // 初始容量为100,假设已知要存储约100个元素

// 不推荐,默认初始容量较小,可能导致多次扩容
List<int> numbers = new List<int>();
for (int i = 0; i < 100; i++)
{
    numbers.Add(i);
}
  1. 使用 Span 处理缓冲区 :在 IO 操作和内存处理中,Span<T>提供了对连续内存块的高效、安全访问,能减少数据复制。例如,在处理字节数组时,传统方式可能会进行多次不必要的内存复制,而使用Span<T>可以直接操作内存,避免这些开销。比如:
csharp 复制代码
byte[] data = new byte[1024];
// 传统方式,可能存在多次内存复制
for (int i = 0; i < data.Length; i++)
{
    data[i] = (byte)i;
}

// 使用Span优化
Span<byte> span = data;
for (int i = 0; i < span.Length; i++)
{
    span[i] = (byte)i;
}

(二)常见错误与解决方案

  1. 索引越界异常 :在 C# 中,数组的有效索引范围是从 0 到Length - 1。当访问的索引小于 0 或大于等于Length时,就会抛出IndexOutOfRangeException。导致索引越界的原因通常有:索引超出有效范围、动态计算索引错误、多线程环境下的并发问题以及循环终止条件错误等。例如:
csharp 复制代码
int[] numbers = { 1, 2, 3, 4, 5 };
int number = numbers[10];  // 索引10超出范围,抛出异常

为避免索引越界异常,可采取以下措施:使用条件语句检查索引有效性,在访问数组元素之前,使用条件语句检查索引是否在有效范围内;使用try - catch块捕获异常,虽然不推荐将try - catch作为常规控制流的一部分,但在某些情况下,可以使用它来捕获并处理数组越界异常,避免程序崩溃;使用 LINQ 的ElementAtOrDefault方法,该方法可以安全地访问数组元素,如果索引超出范围,则返回指定类型的默认值(如 0、null等);使用循环正确遍历数组,确保循环的终止条件正确;在多线程环境中使用同步机制,确保线程安全;对用户输入进行验证,如果索引来源于用户输入,应对输入进行验证,确保其有效性。比如:

csharp 复制代码
int[] numbers = { 1, 2, 3, 4, 5 };
int index = 10;
if (index >= 0 && index < numbers.Length)
{
    Console.WriteLine(numbers[index]);
}
else
{
    Console.WriteLine("索引超出范围");
}
  1. 多维数组与交错数组混淆:多维数组(矩形数组)是一个由多个数组组成的数组,其每个子数组的类型和子数组中元素个数大小都是一致的;而交错数组(锯齿数组)中的每个子数组的类型虽然一般也是相同的,但是每个子数组中的元素个数却可以是不同的。在实际应用中,应明确数据结构需求,对于规则数据,如矩阵、表格数据等,使用多维数组;对于不规则数据,如不同长度的文本段落、动态报表行数据等,使用交错数组。例如:
csharp 复制代码
// 多维数组,适用于规则矩阵数据
int[,] matrix = new int[3, 4];

// 交错数组,适用于不规则数据
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2 };
jaggedArray[1] = new int[] { 3, 4, 5 };
jaggedArray[2] = new int[] { 6 };
  1. 引用类型数组的浅拷贝问题 :对于包含引用类型的数组,使用Clone方法或Array.Copy方法进行复制时,执行的是浅拷贝,即只复制引用,而不复制引用所指向的对象。这可能导致修改一个数组中的对象时,另一个数组中的对应对象也被修改。为避免此问题,对包含引用类型的数组,需手动实现深拷贝。例如:
csharp 复制代码
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        Person[] originalArray = { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 30 } };
        Person[] shallowCopyArray = (Person[])originalArray.Clone();
        shallowCopyArray[0].Age = 26;  // 会同时修改originalArray中对应元素的Age
        // 手动实现深拷贝
        Person[] deepCopyArray = new Person[originalArray.Length];
        for (int i = 0; i < originalArray.Length; i++)
        {
            deepCopyArray[i] = new Person { Name = originalArray[i].Name, Age = originalArray[i].Age };
        }
        deepCopyArray[0].Age = 27;  // 不会影响originalArray中对应元素的Age
    }
}

(三)代码规范与可读性

  1. 显式类型声明 :在声明数组时,推荐使用显式类型声明,如int[] numbers,而非隐式的var numbers。虽然var关键字在某些情况下可以简化代码,但显式类型声明能提高代码的可读性,让其他开发者更容易理解数组的类型。例如:
csharp 复制代码
// 推荐,显式类型声明,可读性强
int[] scores = { 85, 92, 78 };

// 不推荐,可读性较差,需查看初始化值才能确定类型
var scores = new[] { 85, 92, 78 };
  1. 合理使用命名空间 :对常用数组操作(如Array.Sort),确保引入System命名空间。在 C# 中,System命名空间包含了许多基本类型和常用的类库,Array类就位于System命名空间中。如果未引入System命名空间,在使用Array类的方法时会出现编译错误。例如:
csharp 复制代码
using System;  // 引入System命名空间
class Program
{
    static void Main()
    {
        int[] numbers = { 5, 2, 1, 3, 4 };
        Array.Sort(numbers);  // 使用Array类的Sort方法
    }
}
  1. 注释关键逻辑:对复杂的数组操作(如矩阵运算)添加注释说明算法逻辑。在进行复杂的数组操作时,如矩阵乘法、多维数组的复杂遍历等,添加注释可以帮助其他开发者理解代码的功能和实现思路,提高代码的可维护性。例如:
csharp 复制代码
// 实现矩阵乘法
int[,] matrixA = { { 1, 2 }, { 3, 4 } };
int[,] matrixB = { { 5, 6 }, { 7, 8 } };
int rowsA = matrixA.GetLength(0);
int colsA = matrixA.GetLength(1);
int colsB = matrixB.GetLength(1);
int[,] result = new int[rowsA, colsB];
// 遍历矩阵A的行
for (int i = 0; i < rowsA; i++)
{
    // 遍历矩阵B的列
    for (int j = 0; j < colsB; j++)
    {
        // 计算矩阵A的第i行与矩阵B的第j列的点积
        for (int k = 0; k < colsA; k++)
        {
            result[i, j] += matrixA[i, k] * matrixB[k, j];
        }
    }
}
相关推荐
yesyesido8 分钟前
智能文件格式转换器:文本/Excel与CSV无缝互转的在线工具
开发语言·python·excel
_200_10 分钟前
Lua 流程控制
开发语言·junit·lua
环黄金线HHJX.10 分钟前
拼音字母量子编程PQLAiQt架构”这一概念。结合上下文《QuantumTuan ⇆ QT:Qt》
开发语言·人工智能·qt·编辑器·量子计算
王夏奇11 分钟前
python在汽车电子行业中的应用1-基础知识概念
开发语言·python·汽车
He_Donglin11 分钟前
Python图书爬虫
开发语言·爬虫·python
星融元asterfusion20 分钟前
AsterNOS SONiC基于YANG模型的现代网络管理:从CLI到gNMI的演进
开发语言·sonic·yang
web3.088899923 分钟前
1688商品详情API接口深度解析
开发语言·python
欧阳天风27 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
散峰而望28 分钟前
【算法竞赛】顺序表和vector
c语言·开发语言·数据结构·c++·人工智能·算法·github
小鸡脚来咯31 分钟前
Java字符串详解
java·开发语言