C#高级编程(第10版) C# 6 & .NET Core 1.0 (.NET开发经典名著)
上QQ阅读APP看书,第一时间看更新

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"}
        };