第十六章 数组
目录:
16.1 初始化数组
16.2 数组转型
16.3 所有数组都隐式派生自System.Array
16.4 所有数组都隐式实现IEnumerable和IList
16.5 数组的传递和返回
16.7 数组的内部工作原理
16.8 不安全的数组访问和固定大小的数组
数组是允许将多个数组项作为集合来处理的机制。CLR支持一维,多维和交错数组(数组构成的数组)。所有数组类型都隐式的从System。Array抽象类派生,后者又派生自System.Object。这意味着数组始终是引用类型,是在托管堆上分配的。在应用程序的变量或字段中,包含的是对数组的引用,而不是包含数组本身的元素。
为了符合“公共语言规范”的要求,所有数组都必须是0基数组(最小索引为0)。每个数组都关联了一些额外的开销信息。这些信息包括数组的秩,数组每一维的下限(几乎总是0)和每一维长度。开销信息还包含数组的元素类型。
16.1 初始化数组
大括号中的以逗号分隔的数据项称为数组初始化器。每个数据项都可以使一个任意复杂度的表达式;在多维数组的情况下,则可以使一个嵌套的数组初始化器。
16.2 数组转型
Array.Copy方法:
将值类型的元素装箱为引用类型的元素。
将引用类型的元素拆箱为值类型元素。
加宽CLR基元值类型。
在两个数组之间复制时,如果仅从数组类型证明不了两者的兼容性,就根据需要对元素进行向下类型转换。
将数组从一种类型转换为另一种类型。成为-数组协变性。
16.3 所有数组都隐式派生自System.Array
声明一个数组,CLR会自动为AppDomain创建类型。改类型隐士派生自System.Array类型。
16.4 所有数组都隐式实现IEnumerable和IList
16.5 数组的传递和返回
数组作为实参传递给方法时,实际传递的是对该数组的引用。因此被调用的方法能修改数组中的元素。如果不想被修改,必须生成数组的拷贝并将拷贝传给方法。
有的方法返回对数组的引用。如果方法返回的是对字段所维护的一个内部数组的引用,就必须决定是否想让改方法的调用者直接访问这个数组及元素。方法应该构造一个新数组,并调用Array.Copy返回对新数组的引用。
16.6 创建下限非零的数组
调用数组的静态CreatInstance方法来动态创建自己的数组。
16.7 数组的内部工作原理
CLR内部支持两种不同的数组:
下限为0的一维数组。这些数组有时称为SZ(一维0基)数组或向量。
下限未知的一维或多维数组。
不安全数组访问技术不足:
相较于其他技术,处理数组元素的代码更复杂,不容易读和写,因为要使用C#fixed语句,要执行内存地址计算。
计算过程中出错,可能访问到不属于数组的内存。这会造成计算错误,损坏内存数据,破坏类型安全性,并可能造成安全漏洞。
因为这些潜在的问题,CLR禁止在降低了安全级别的环境中运行不安全代码。
16.8 不安全的数组访问和固定大小的数组
不安全代码允许访问:
堆上的托管数组对象中的元素
非托管堆上的数组中的元素
线程栈上的数组中的元素
如果性能是首要目标,请避免在堆上分配托管的数组对象。相反,在线程栈上分配数组。
由于数组是引用类型,所以结构中定义的数组字段实际只是指向数组的指针或引用。数组本身在结构的内部的外部。也可以直接嵌入结构:
类型必须是结构(值类型);不能在类中嵌入数组。
字段或其定义结构必须用usafe关键字标记。
数组字段必须用fixed关键字标记
数组必须是一维0基数组。
要和非托管代码进行互操作,而且非托管数据结构也有一个内联数组,就特别适合使用内联得数组。但内联数组也能用于其他地方。