这两天看了<CLR via C#>的第16章,数组部分,就将里面的给记录一边,方法日后查询。
所有的数据继承自System.Array抽象类,后者派生自System.Object类;
一、初始化
int[] integers = new int[10] //在托管堆上分配容纳10个未装箱的int32所需的内存块;
control[] controls = new control[10]; //在托管堆上分配容纳10个control的引用 需要的空间,每个control初始化为null;
二维数组:
Double[,] ds = new Double[10,20]
交错数组:
Double[][] ds = new Double[10][]; //创建10个指向double[]数组的引用;
二、数组类型转换:
两个数组类型必须维数相同,而且从源类型到目标类型必须存在一个隐式或显式的转换 ;但是CLR不允许将值类型元素的数组转型为其他任何类型;
三、数据复制:
1)、Array.Copy方法:
将元素从一个数组复制到另一个数组,并且能够处理内存的重叠区域;但是只是进行浅复制;在复制时也进行必要的类型转化与装(拆)箱;
2)、System.Buffer.BlockCopy方法:
速度快,只支持基元类型;
方法的Int32参数代表的是数组中的字节偏移量,而非元素索引;即它实现的是按位兼容的形式进行复制;
按位兼容:指类型在托管堆与非托管堆上的内存上有相同的表示;
3)、System.Array.ConstrainedCopy方法:
保证在不破坏目标数组中的数据的前提下完成复制,或者是抛出一个异常。
它要求源数组类型要么与目标数据类型相同,要么派生自目标数据的元素类型。
它不执行任何装箱、拆箱或向下类型转换(从基类到派生类)。
System.Array继续了IEnumerable,ICollection,IList接口,因为这些接口将所有元素视为Object,而没有实现这些接口的泛型类型;
而在0基1维的数据的创建时,实现相应类型的IEnumerable<T>,ICollection<T>,IList<T>接口;
如果数据包含值类型的元素,则数组类型不会为元素的基类型实现接口;
示例:
Object
Array
Object[]
Stream[]
FileStream[]
则FileStream[] fsArray; 这条语句执行时,将会实现:IEnumerable(ICollection,IList)<FileStream(Stream,Object)>这些类型;
但是执行如下的代码:
DateTime[] dtArray时;
只会实现:IEnumerable(ICollection,IList)<DateTime>这些类型,而不会为DateTime的基类型实现;
这是因为值类型数组与引用类型数组的内存布局不同。
四、创建下限非零的数组:
Int32 lowerBounds = {2005,1}
Int32 lengths = {5,4}
Decimal[,] qrArray = (Decimal[,[)Array.CreateInstance(typeof(Decimal), lengths, lowerBounds);
五、访问数组的性能
访问一维0基数组比访问非0基一维或多维数组快。原因:
1)、特殊的IL指令对0维数组进行优化,导致JIT编译器生成优化代码;
示例:
int32[] a = new Int32[5];
for(Int32 index = 0; index<a.Length; index++){
//op
}
在此有两个优化:
其一:对数据Length属性的访问(这里实际上是执行GetLength的方法调用),JIT对0基一维数据只执行一次,并缓存在本地的临时变量中;
其二:JIT在执行for循环的访问时,会先判断所有访问的数据是否在数组的有效范围之内。如
在执行循环时,先判断(0>=a.GetLowerBound(0)) && ((Length-1)<=a.GetUpperBound(0))。这个检查出现在循环之前,如果在数组的有效范围之内,JIT将不会在循环内部生成代码验证每一次数组访问都在有效范围之内。
在执行循环时,先判断(0>=a.GetLowerBound(0)) && ((Length-1)<=a.GetUpperBound(0))。这个检查出现在循环之前,如果在数组的有效范围之内,JIT将不会在循环内部生成代码验证每一次数组访问都在有效范围之内。
为增强性能,可能考虑用交错数组代替多维数组;
从访问的角度看,多维数组比交错数组慢;但是从创建与初始化角度看:由于创建交错数据时,要求在堆上为每一堆分配一个对象,这造成垃圾回收器的周期性活动。因此,如果是创建一个”多维数组“,且不会频繁访问,采用多维数组;反而采用交错数组;
六、不安全数组访问
数据的性能主要在于:堆上分配内存。
可通过实现在线程栈上分配,在每次方法返回时将自动释放内存,因此,不能将此数组指针传给大多数FCL的方法。实现方法:
unsafe{
Char *pc = stackalloc Char[10];
}
可在unsafe结构中包含数据的类型满足如下要求:
类型必须是结构(值类型);不能在引用 类型中嵌入数組;
字段或其定义结构必须用unsafe关键字标记;
数据字段必须用fixed关键字标记;
数组必须是一维0基的;
数组的类型可以是:Boolean,Char,Sbyte,Byte,int16,int32,uint16,uint32,int64,uint64,Single, Double.
示例:
unsafe struct CharArray{
public fixed Char Characters[20];
}