概念

数组是一个存储雷同数据类型的固定大小的程序汇合,容许将多个数据项当作一个汇合来解决的数据结构。

数组的申明
//dataType[] arrayNameint[] intArray;//初始化intArray = new int[3];

第一行代码申明了一个int类型的数组,此时并没有调配数组,所以默认赋值为null。
第二行代码将会在托管堆上调配包容3个未装箱的int值的内存块,并默认为所有值赋值为0。

援用类型数组
class People { }People[] peopleArray;//全副初始化为null,未调配值,相当于创立了一组援用peopleArray = new People[3];

因为数组是援用类型,所以不论是值类型数组还是援用类型数组,都是存在于托管堆中的,值类型数组和援用类型数组在托管中的状况如图:

上图的People数组,就是执行了以下代码的后果

peopleArray[0] = new Man();peopleArray[1] = new Woman();peopleArray[2] = new Child();
数组类型
  • 0基数组(索引从0开始)
  • 多维数组(包含一维数组)
  • 交织数组(由数组形成的数组)
申明多维数组
int[,] intArrays = new int[2, 2]; //二维数组string[,,] strArrays = new string[1, 2, 2]; //三维数组
申明交织数组
int[][] interleavedArray = new int[3][];interleavedArray[0] = new int[1];interleavedArray[1] = new int[2];interleavedArray[2] = new int[3];
将数组的申明与初始化合并
int[] intArray = new int[] { 1, 2, 3 };int[,] intArrays = new int[,] { { 1, 2 } };//二维//简化var intArray = new int[] { 1, 2, 3 };//持续简化var intArray = new[] { 1, 2, 3 };

*在应用隐式类型的数组性能时(不指定数组的具体类型),编译器会查看用于初始化数组元素的类型,并抉择所有元素最靠近的独特基类作为数组的类型,如果其中某一个元素与其它不具备雷同类型或基类型编译器将报错:找不到隐式类型数组的最佳类型。

隐式类型数组和匿名类型相结合
var peoples = new[] { new { Name = "A" }, new { Name = "B" } };//输入//A//Bforeach (var item in peoples)    Console.WriteLine(item.Name);

*应用隐式类型数组需保障类型的同一性

数组协变性

数组协变性也称数组的转型,CLR容许将援用类型的数组从一种类型隐式转换成另一种类型,数组转型须要具备以下条件:

  • 两个数组类型必须维数雷同
  • 源类型到指标类型必须存在一个显示或隐式的转换

*CLR不容许将值类型元素的数组转型成其它任何类型

People[] peopleArray;object[] objectArray = peopleArray; //正确int[,] intArrays = new int[,] { { 1, 2 } };object[,] objectArray = intArrays; //谬误
Array.Copy变相实现值类型元素数组的转型
var intArray = new[] { 1, 2, 3 };object[] objectArray = new object[intArray.Length];Array.Copy(intArray, objectArray, intArray.Length);
Array.Copy办法能执行以下转换:
  • 将值类型的元素装箱成为援用类型的元素
  • 将援用类型的元素拆箱成为值类型的元素

*因为Copy办法会产生装箱或者拆箱,毋庸置疑会对性能产生影响,应用Array.ConstrainedCopy办法将不会产生装箱拆箱操作,同样也有绝对的限度:源数组类型元素与指标数组元素类型雷同或者派生自同一个基类。

数组的传递和返回倡议

数组是援用类型,所以数组作为参数传递给办法时,传递的其实是援用,所以在办法外部对数组进行操作的后果将会间接影响源数据。
办法在返回一个数组时,最好不要返回null,即便这个数组是空数组,起因如下:

int[] intArray = new int[] { };//遍历空数组foreach (var item in intArray)    Console.WriteLine(item);    int[] intArray = null;//遍历值为null的数组if (intArray != null){    foreach (var item in intArray)    Console.WriteLine(item);}

应用CreateInstance创立上限非0的数组,该办法容许指定数组元素的类型,数组的维数,每一维的上限和每一维的元素数目

int[] lowerBounds = { 3, 1 }; //每一维数数组的上限int[] lengths = { 3, 2 }; //每一维数数组的长度Decimal[,] decimalArray = (Decimal[,])Array.CreateInstance(typeof(Decimal), lengths, lowerBounds);decimalArray[3, 1] = 1;decimalArray[3, 2] = 2;for (int i = decimalArray.GetLowerBound(0); i <= decimalArray.GetUpperBound(0); i++){    for (int j = decimalArray.GetLowerBound(1); j <= decimalArray.GetUpperBound(1); j++)    {        Console.WriteLine("i : {0}, j : {1}, value : {2}", i, j, decimalArray[i, j]);    }}

输入:

创立一维的非0基数组
int[] myArrLen = { 4 };int[] myArrLow = { 2 };var myArrayTwo = Array.CreateInstance(typeof(int), myArrLen, myArrLow);//赋值myArrayTwo.SetValue(1, 2);myArrayTwo.SetValue(2, 3);myArrayTwo.SetValue(3, 4);myArrayTwo.SetValue(4, 5);//取值myArrayTwo.GetValue(4);

*必须应用Array的SetValue与GetValue办法拜访一维非0基数组的元素

拜访数组的性能

在遍历0基一维数组的时候,通常须要拜访数组的Length,因为JIT编译器晓得Length是Array的属性,所以不会把它当作一个办法每次循环都调用,而会在第一次循环的时候调用一次,并将值存储到一个长期变量中,之后每次循环查看这个长期变量即可,从而晋升性能。
而拜访非0基一维数组或多维数组的时候,JIT编译器不会将索引查看从循环中抽出来,导致每次循环都得验证指定的索引,从而影响代码的速度,这也是性能不如0基一维数组的起因。

应用交织数组和unsafe晋升数组拜访性能
//多维数组int[,] intArrays = new int[10000,10000];var sw = Stopwatch.StartNew();var sum = 0;//一般平安遍历for (int i = 0; i < 10000; i++)    for (int j = 0; j < 10000; j++)        sum += intArrays[i, j];Console.WriteLine("遍历二维数组 耗时: {0}", sw.Elapsed);//交织数组int[][] interleavedArray = new int[10000][];sw = Stopwatch.StartNew();//一般平安遍历for (int i = 0; i < 10000; i++)    for (int j = 0; j < 10000; j++)        sum += intArrays[i, j];Console.WriteLine("遍历交织数组 耗时: {0}", sw.Elapsed);//unsafe遍历sw = Stopwatch.StartNew();Test(intArrays);Console.WriteLine("unsafe遍历数组 耗时: {0}", sw.Elapsed);private static unsafe void Test(int[,] arg){    var sum = 0;    fixed(Int32 * pi = arg)    {        for (int i = 0; i < 10000; i++)        {            var temp = i * 10000;            for (int j = 0; j < 10000; j++)                sum += pi[temp + j];        }    }}

测试后果能够得出:
遍历二维数组最慢,交织数组平安遍历工夫少于平安遍历二维数组的耗时,然而因为创立交织数组须要在堆上为每一维调配一个对象,造成垃圾回收,所以创立交织数组所破费的工夫要大于创立二维数组的工夫。
因而咱们能够在须要创立大量多维数组时,而不会频繁拜访数组中的元素,那么抉择创立多维数组性能较好。反之如果多维数组只需创立一次,并且须要频繁拜访数组中的元素,那么就是用交织数组性能来得更好一些。

显然不平安代码是性能最好的,然而应用该技术同样存在危险

  • unsafe间接操作内存,存在毁坏类型平安的危险
  • 代码简单,不易写
  • 应用fixed,须要执行内存地址计算可读性升高
  • 如果内存地址计算错误,会损坏内存数据,毁坏类型平安