CLR笔记:13.数组
CLR支持一维/多维/交错数组。
两种声明方式:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
注意,声明不要给与数组长度,因为此时还不分配内存;new时要指定长度。
将数组声明为Array和像String[]这样带中括号的,效果是一样的,只是前者更灵活,当然类型不安全,可能会有运行期错误。
所有数组都隐式派生于System.Array,后者派生自System.Object,所以数组是引用类型。
值类型的数组,new时,会创建实际的对象,所有元素都初始化为0;
引用类型的数组,new时,只是创建引用,不会创建实际的对象。
CLS要求数组必须是0基数组。
每个数组都关联了一些额外的信息,称为开销信息(Overhead)。
JITer只在执行一次循环之前检查一次数组边界,而不是每次循环都这么做。
13.1 数组的类型转换
对于引用类型的数组,两个数组类型必须维数相同,并且从源元素类型到目标元素类型,必须存在一个隐式(向父类转)或显示(向子类转)的类型转换。
对于值类型的数组,不能将其转换为其他任何一种类型——使用Array.Copy方法替代:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
Array.Copy还可以再复制每个数组元素时进行必要的类型转换:
*将值类型的元素装箱为引用类型的元素
*将引用类型的元素拆箱为值类型的元素
*加宽CLR基元值类型,Int32到Double
*如果两个数组的类型不兼容(从Object[]转型为IFormattable[]),就对元素进行向下类型转换。
数组的协变性:将数组从一种类型转换为另一种类型,——会有性能损失,如下代码,会在运行期报错:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
如果只是需要将数组中的某些元素复制到另一个数组,可以使用System.Buffer的BlockCopy()方法,执行速度比Array.Copy快,但是只支持基元类型,不具有转型能力。
13.3 所有数组都隐式实现IEnumerable,ICollection,IList
对于泛型接口,因为多维数组和非0基数组的问题,System.Array并不完全实现。
只有一维0基的引用类型的数组,在创建时,会自动实现了IEnumerable<T>,ICollection<T>,IList<T>,T为这个数组的类型;还为T的所有基类型B实现了IEnumerable<B>,ICollection<B>,IList<B>接口。
如FileStream[] fsArray,会自动实现了IEnumerable<FileStream>,ICollection<FileStream>,IList<FileStream>,同时会实现IEnumerable<Stream>,ICollection<Stream>,IList<Stream>,IEnumerable<Object>,ICollection<Object>,IList<Object>,所以fsArray可以作为参数传递给以下方法:
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
但是,对于一维0基的值类型的数组,在创建时,会自动实现了IEnumerable<T>,ICollection<T>,IList<T>,T为这个数组的类型;但是不会为其基类实现接口。如DateTime[] dtArray;这个值类型dtArray不能传递给上面的M3方法。
13.4 数组的传递和返回
Array.Copy返回的是浅Copy。
数组最好是0长度,而不是null
13.5 非0基数组
使用Array.CreateInstance方法,可以指定数组中元素类型,数组维数,数组下界,数组每一维中元素个数,有若干重载,如下:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
可以使用GetLowerBound(维数);与GetUpperBound(维数);获取数组的边界
在一位数组fsArray中,可以使用fsArray数组对象的GetValue()和SetValue()方法来操作数组,但是比较慢。
13.6 数组访问性能
对于1维数组,0基的typeof为String.String[];非零基为String.String[*]
对于多维数组,都会显示String.String[,]。在运行时,CLR将多维数组视为非零基数组。
访问1维零基数组 比 非零基1维数组 和 多维数组 速度快很多。这是因为:
1.有特殊的IL指令,用于处理1维零基数组,而不用在索引中减去偏移量
2.JIT会将索引范围检查代码从循环中取出,从而只执行一次检查。
关于for循环遍历数组:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
Length是一个数组属性,调用一次后,JIT会将结果存入一个临时变量,以后每次循环迭代访问的都是这个变量,而不是再次计算长度,从而速度更快。
在循环前,JIT编译器会自动生成代码检测有效范围:if((Length-1) < a.GetUpperBound(0)),只会检测一次。
如果将Length保存在一个本地变量,在for循环时每次都会比较该变量,反而会损害性能。
以上只适用于0基数组。非零基数组中,JIT编译器在循环中,每次都要检查制指定索引范围是否有效,此外还要从指定索引减去数组下界,从而降低了性能。
提升性能的两个办法:
使用交错数组(数组的数组)来代替多维数组。
使用非安全数组代替非零基数组,在访问数组时关闭索引边界检查——只适用于基元值类型。
使用非安全代码访问2维数组:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
13.7 非安全数组和内嵌数组
非安全数组可以访问以下元素:
托管堆上的数组
非托管堆的数组
线程堆栈上的数组
在性能第一的前提下,避免在堆上分配数组对象,应该在线程堆栈上分配——使用stackalloc
stackalloc只适用于创建1维0基值类型数组,而且值类型中决不能包括任何引用类型。见以下方法,将一个字符串倒置:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
在struct中定义数组,一般来说,数组本身在struct的外部。
要使数组内嵌在struct中,要遵从:
1.struct要用unsafe标记
2.数组字段要用fixed标记
3.数组必须是1维0基的
4.数组类型必须是基元值类型
采用内嵌数组实现的方法,将一个字符串倒置:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)