《CLR via C#》 数组

CLR支持一维、多维和交错数组。所有数组都隐式地从System.Array抽象类派生,后者又派生自System.Object。这意味着数组始终是引用类型,从托管堆分配。

一维数组

1int[] arr;          // 声明一个数组引用
2arr = new int[100]; // 创建含有100个int的数组,并全部初始化为0

实际上,除了数组元素,数组对象占据的内存块还包含一个类型对象指针、一个同步块索引和一些额外的成员。

注意

应尽可能的使用一维0基数组。因为其性能是最佳的,可以使用一些特殊的IL指令来处理。

多维数组

1double[,] darr = new double[10, 20];    // 二维数组
2string[,,] sarr = new string[5, 3, 10]; // 三维数组

交错数组

交错数组:数组构成的数组。0基一维交错数组的性能和普通向量一样好。不过访问交错数组的元素意味着必须进行两次或更多此访问

1int[][] arr = new int[3][]; // 创建由多个int数组构成的一维数组
2arr[0] = new int[10];       // arr[0]引用一个含有10个int实例的数组
3arr[1] = new int[20];       // arr[1]引用一个含有20个int实例的数组
4arr[2] = new int[30];       // arr[2]引用一个含有30个int实例的数组

数组初始化

1// 大括号中以逗号分隔的数据项称为数组初始化器
2String[] names = new String[] {"A", "B"};
3// 隐式类型数组。
4var kids = new[] {new {Name="A"}, new {Name="B"} };

数组转型

CLR不允许将值类型元素的数组转型为其它任何类型。(不过可用Array.Copy方法创建新数组并在其中填充元素来模拟这种效果)

1int[] iarr = new int[5];
2// object[] oarr = (object[]) iarr; // 编译报错
3
4// 创建一个新数组,使用Array.Copy将源数组中的每个元素转型为目标数组中的元素类型,并把它们复制过去。(Array.Copy是浅拷贝)
5object[] oarr = new object[iarr.Length];
6Array.Copy(iarr, oarr, iarr.Length);

注意

可用System.BufferBlockCopy方法将元素复制到另一个数组,它比Array.Copy方法块,但只支持基元类型。

数组的隐式继承

  • 所有数组都隐式派生自System.Array
  • 所有数组都隐式实现IEnumerable,ICollection,IList

创建一维0基数数组类型时,CLR自动使数组类型实现IEnumerable<T>,ICollection<T>IList<T>。同时,还为数组类型的所有基类型实现这三个接口,必须是引用类型

1public class A {}
2public class B : A {}
3B[] b = new B[2]; // 会为A[]和B[]自动实现其三个接口

注意

数组作为实参传递给方法时,实际传递的是数组的引用

创建下限非零开始的数组

利用数组的静态CreateInstance方法来动态创建数组。CreateInstance为数组分配内存,将参数信息保存到数组的内存块开销部分,然后返回对该数组的引用。如果数组为多维数组。可以把数组引用转型为ElementType[]变量来对数组元素访问。如果只有一维,要求必须用Array.GetValueArray.SetValue访问数组

1// 创建二维数组[2005...2009][1...4]
2int[] lowerBounds = {2005, 1};
3int[] lengths = {5, 4};
4int[,] arr = (int[,]) Array.CreateInstance(typeof(int), lengths, lowerBounds);

数组的内部工作原理

CLR内部实际支持两种不同的数组

  • 下限从0开始的一维数组。
  • 下限开始未知的一维或多维数组。

注意

访问一维0基数组的元素比访问非0基一维或多维数组的元素稍快。如果很关心新能可以用交错数组来代替矩阵数组。