7.2 简单数组
如果需要使用同一类型的多个对象,就可以使用数组。数组是一种数据结构,它可以包含同一类型的多个元素。
7.2.1 数组的声明
在声明数组时,应先定义数组中元素的类型,其后是一对空方括号和一个变量名。例如,下面声明了一个包含整型元素的数组:
int[] myArray;
7.2.2 数组的初始化
声明了数组后,就必须为数组分配内存,以保存数组的所有元素。数组是引用类型,所以必须给它分配堆上的内存。为此,应使用new运算符,指定数组中元素的类型和数量来初始化数组的变量。下面指定了数组的大小。
myArray = new int[4];
注意:值类型和引用类型请参见第3章。
在声明和初始化数组后,变量myArray就引用了4个整型值,它们位于托管堆上,如图7-1所示。
图7-1
注意:在指定了数组的大小后,如果不复制数组中的所有元素,就不能重新设置数组的大小。如果事先不知道数组中应包含多少个元素,就可以使用集合。集合请参见第11章。
除了在两个语句中声明和初始化数组之外,还可以在一个语句中声明和初始化数组:
int[] myArray = new int[4];
还可以使用数组初始化器为数组的每个元素赋值。数组初始化器只能在声明数组变量时使用,不能在声明数组之后使用。
int[] myArray = new int[4] {4, 7, 11, 2};
如果用花括号初始化数组,则还可以不指定数组的大小,因为编译器会自动统计元素的个数:
int[] myArray = new int[] {4, 7, 11, 2};
使用C#编译器还有一种更简化的形式。使用花括号可以同时声明和初始化数组,编译器生成的代码与前面的例子相同:
int[] myArray = {4, 7, 11, 2};
7.2.3 访问数组元素
在声明和初始化数组后,就可以使用索引器访问其中的元素了。数组只支持有整型参数的索引器。
通过索引器传递元素编号,就可以访问数组。索引器总是以0开头,表示第一个元素。可以传递给索引器的最大值是元素个数减1,因为索引从0开始。在下面的例子中,数组myArray用4个整型值声明和初始化。用索引器对应的值0、1、2和3就可以访问该数组中的元素。
int[] myArray = new int[] {4, 7, 11, 2}; int v1 = myArray[0]; // read first element int v2 = myArray[1]; // read second element myArray[3] = 44; // change fourth element
注意:如果使用错误的索引器值(大于数组的长度),就会抛出IndexOutOfRangeException类型的异常。
如果不知道数组中的元素个数,则可以在for语句中使用Length属性:
for (int i = 0; i < myArray.Length; i++) { WriteLine(myArray[i]); }
除了使用for语句迭代数组中的所有元素之外,还可以使用foreach语句:
foreach (var val in myArray) { WriteLine(val); }
注意:foreach语句利用了本章后面讨论的IEnumerable和IEnumerator接口,从第一个索引遍历数组,直到最后一个索引。
7.2.4 使用引用类型
除了能声明预定义类型的数组,还可以声明自定义类型的数组。下面用Person类来说明,这个类有自动实现的属性Firstname和Lastname,以及从Object类重写的ToString()方法(代码文件SimpleArrays/Person.cs):
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() => $"{FirstName} {LastName}"; }
声明一个包含两个Person元素的数组与声明一个int数组类似:
Person[] myPersons = new Person[2];
但是必须注意,如果数组中的元素是引用类型,就必须为每个数组元素分配内存。若使用了数组中未分配内存的元素,就会抛出NullReferenceException类型的异常。
注意:第14章介绍了错误和异常的详细内容。
使用从0开始的索引器,可以为数组的每个元素分配内存:
myPersons[0] = new Person { FirstName="Ayrton", LastName="Senna" }; myPersons[1] = new Person { FirstName="Michael", LastName="Schumacher" };
图7-2显示了Person数组中的对象在托管堆中的情况。myPersons是存储在栈上的一个变量,该变量引用了存储在托管堆上的Person元素对应的数组。这个数组有足够容纳两个引用的空间。数组中的每一项都引用了一个Person对象,而这些Person对象也存储在托管堆上。
图7-2
与int类型一样,也可以对自定义类型使用数组初始化器:
Person[] myPersons2 = { new Person { FirstName="Ayrton", LastName="Senna"}, new Person { FirstName="Michael", LastName="Schumacher"} };