数组

数组的类型

可以把矩形数组和交错数组想象为一个特殊的一维数组,该数组的每个元素都是一个数组,叫做子数组,如果子数组的长度都相同,则为矩形数组,反之则为交错数组。
数组类型派生自System.Array类型,Array是一个抽象类,因此数组也是一个类(class)类型,因为类属于引用类型,因此数组也是引用类型。当基类和派生类之间需要进行转换时,会进行隐式转换和显式转换的过程。

int[] array1 = new int[10];
Array array = array1; //隐式转换

int[] array2 = (int[])array; //显式转换

数组的声明、实例化、初始化

声明

int[] array1; //一维数组
int[ , ] array2;//矩形数组
int[ , , ] array3;//矩形数组
int[][] array4;//交错数组
  • 要声明一个一维数组或矩形数组,首先要确定数组的类型,然后在类型后紧跟一对方括号,最后是数组变量名称,如果是矩形数组,还是确定数组的维数。
  • 方括号代表的是维数说明符,一维数组为[],二维数组为[,],三维数组为[,,],因此矩形数组的维数等于逗号数加1
  • 数组一旦声明,维数就是固定的,但每个维度的长度此时并未确定,需要到数组实例化时才会被确定
  • 数组声明时不能包含数组维度的长度
int[7] array1; //编译错误
int[] array1; //正确

实例化

实例化数组,可以使用new运算符。 int[] array1 = new int[5]; //一维数组

初始化

数组被实例化后,如果没有明确地对数组进行初始化,各元素会被初始化为类型默认值。
实例化时数组维度大小可以省略,编译器可以根据初始值来推断数组维度的大小,还可以进一步简化,根据初始值的类型来推断数组的类型,即数组是隐式类型的。

//声明时初始化
int[] array1 = new int[5] { 1, 2, 3, 4, 5 };
int[] array2 = new int[] { 1, 2, 3, 4, 5 }; //声明初始化一起时,可省略长度
var array3 = new[] { 1, 2, 3, 4, 5 }; //隐式类型数组
 
//声明初始化分开
int[] array4;
array4 = new int[5] { 1, 2, 3, 4, 5 };

//矩形数组
int[,] array5 = new int[3, 3] { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };

交错数组

交错数组是数组的数组

  • 交错数组是由顶级数组和子数组组成
  • 顶级数组是一个一维数组
  • 顶级数组的每个元素都是一个子数组
  • 子数组的维度和长度可以不同
  • 子数组的类型和顶级数组必须相同,即顶级数组是int类型,那么子数组也必须是int类型
  • 子数组的维度可以和顶级数组不同,即子数组可以是多维数组,甚至可以是交错数组

声明

交错数组虽然和矩形数组同属多维数组,但声明方式不一样,矩形数组无论多少维度都使用一对方括号,方括号内使用逗号表示维度数,但交错数组要求多少维度就有多少对方括号。

int[][] array1;        //维度数=2
string[][][] array2; //维度数=3

实例化

实例化交错数组只能指定顶级数组的长度,不能指定子数组的长度

int[][] array1 = new int[2][];
string[][][] array2 = new string[3][][];

初始化

要完成整个交错数组的初始化,必须将各个子数组均实例化

int[][] array1 = new int[2][];
array1[0] = new int[3];
array2[0] = new int[5];

int[][] array2 = new int[2][];
array2[0] = new int[] { 1, 2, 3 };
array2[1] = new int[] { 4, 5, 6, 7, 8 };

int[][] array3 = new int[2][] {
    new int[] { 1, 2, 3 },
    new int[] { 4, 5, 6, 7, 8 }
};

int[][] array4 = new int[][] {
    new int[] { 1, 2, 3 },
    new int[] { 4, 5, 6, 7, 8 }
};

int[][] array5 = {
    new int[] { 1, 2, 3 },
    new int[] { 4, 5, 6, 7, 8 }
};

数组的遍历

使用foreach对数组遍历时,其中的迭代变量是只读的,如果数组元素的类型为引用类型时,虽然不能对其赋值,但可以修改该对象中的数据,而使用for进行遍历时则可以修改值

数组协变

允许将数组类型的派生类型赋给数组元素。

  • 数组类型必须是引用类型,
  • 赋值的类型派生自数组的类型,它们之间可以隐式或显式地转换

数组的浅复制和深复制

浅复制:将原对象中的所有字段逐个复制到一个新对象,如果字段是值类型,则简单地复制一个副本到新对象;如果字段是引用类型,则复制的是引用,改变目标对象中引用类型字段的值将会影响原对象,因为引用类型的字段在新对象和原对象中,指向的都是同一个对象。

深复制:深复制与浅复制不同是对于引用类型字段的处理,深复制将会在对象中创建引用类型字段引用的所有对象,而不仅仅是复制引用,此时字段的引用和原始对象对应字段的引用不同,我们可以随意改变新对象中引用的任何对象,不需要担心会影响原对象对应字段的内容

不只是数组才有浅复制和深复制,对象克隆是Object对象提供的功能,任何一个对象都可以对自身进行浅复制和深复制。

针对数组执行浅复制有两种方法,一种是使用CopyTo,另一种是使用Clone。其中Clone方法继承自System.Object对象,返回一个新的数组对象,不过需要显式类型转换,因为Clone方法返回的是Object类型的对象。CopyTo方法将一个一维数组的所有元素复制到一个已有的数组实例,这一点是和Clone方法之间的一个重要区别。

.Net Framework没有提供对深复制的原生API支持,要对数组进行深复制,使用“序列化/反序列”是个不错的选择。

posted @ 2019-06-05 08:43  Allen2015  阅读(233)  评论(0编辑  收藏  举报