《C#高级编程》【第六章】数组

为了解决大量的同类型元素,于是数组就孕育而生了。数组是具有一定顺序关系的若干对象的集合体,一维数组可以看作是定长的线性表。反之,n为的数组可以看作线性表的推广。从存储结构上来看,数组是一段连续的存储空间。现在我们看看在C#中的数组:

1、普通数组 在C#中普通数组又可以分为一维数组、多维数组和锯齿数组。<1>一维数组 我们现在先看看一维数组的声明语法:

类型[] 变量名;

知道怎么声明了,现在我们继续看看数组的初始化吧,在C#中有4种初始化的方式:

//n为数组长度,an为数组内部元素类型[] 数组名 = new 类型[n]; //为数组分配内存,但是没有赋初值(vs会自动为其赋初值为0)类型[] 数组名 = new 类型[n]{a1, a2, …, an}//初始化,并赋初值类型[] 数组名 = new 类型[]{a1, a2 ,…, an}//还可以不指定数组长度,编译器会自动统计元素个数类型[] 数组名 = {a1, a2,…, an}//C风格的初始化并赋值

访问数组时,以”数组名[i]”的方式访问第 i-1个元素。如果不知道数组的长度,可以使用Length属性。 注意:如果数组中的元素类型是引用类型,就必须为每个元素分配内存。在C#中”类型[]”是一个不可分割的整体,”类型[]”可以看成是 数组类型。<2>多维数组 看完一维数组,现在我们推广到多维数组,声明语法:

类型[,] 数组名;//二维数组类型[,,] 数组名;//三维数组

相信你也发现了吧,方括号内的逗号数 + 1 就是数组的维数。我们以二维数组为例,来看看多维数组的初始化:

int[,] arr = new int[2,3]{{1,2,3},{4,5,6}};

借这个例子我想说明多维数组和一维数组初始化的区别就是,多维数组初始化时,每一维度都必须使用大括号括起来。其余的和一维数组初始化方法一样。

<3>锯齿数组 在使用多维数组的过程中,我们有时并不需要每一维度都一样,于是我们就引入了锯齿数组。(在C++中的Vector也有类似的功能)。上一幅图说明二维数组与锯齿数组的区别:

现在我们看看他的声明语法:

类型[][] 数组名 = new 类型[n][];//n为锯齿数组的维度,后一个方括号为空

我们用一个具体实例来说看看他的使用方法:

int[][] Testarray = new int[2][];Testarray[0] = new int[3]{1,2,3};//当然也可以先不赋初值,建议都先赋初值Testarray[1] = new int[4]{1,2,3,4};

这时候有些人可能会有疑问,每一维度的长度不同,那样怎么简单的遍历整个数组呢?这时Length属性就可以发挥它的优势了。我们以上述的为例:

for(int i = 0; i < Testarray.Length; i++){for(int j = 0; j < Testarray[i].Length; j++){//TODO:}}

<4>数组作为参数 既然我们将数组看成一个类型,那么它自然也是可以作为参数传递给方法,也可以从方法中返回。C#数组还支持协变,但是数组协变只能用于引用类型,不能用于值类型。<5>数组段ArraySegment<T>ArraySegment<T>可以和数组之间建立一个映射,直接针对数组的某一片段进行操作,其操作后的结果会直接反映在数组上,反之数组上的变化也会反映到数组段上。我们来看看具体的使用吧:

ArraySegment<int> Test = new ArraySegment<int>(arr, 1, 4);

上述例子,表示Test,从arr[1]开始引用了4个元素。Test.Offset就表示第一个引用的元素,也就是arr[1]。

2、Array类 我们之前使用方括号声明数组,实际上就是隐式的使用了Array类。换一个角度看,我们使用的,例如:int[], double[] 我们都可以把他们看成是派生自Array的子类,这样我们可以使用Array为数组定义方法和属性。<1>创建数组 Array是抽象类,所以不能实例化。但是可以使用静态方法CreateInstance()来创建数组。因为CreateInstance()有多个重载版本,我们就其中一个为例:

//创建一个int型,长度为5的数组,TestArray Test = Array.CreateInstance(typeof(int), 5);//我们将Test[3]的值,赋值为5Test.SetValue(5, 3);//我们要返回 Test[3]的值Test.GetValue(3);//将它变为int[]的数组int[] T1 = (int[])Test;

<2>复制数组 我们可以使用Clone()方法来复制数组,但是如果数组是引用类型那么就只能复制对方的引用。如果数组是值类型,那么才能完整的将对方复制过来。我们还可以使用Copy()方法创建浅表副本。注意:Clone()和Copy()最大的区别:Copy()方法必须使用与原数组相同阶数且有足够的元素空间,但是Cone()方法会创建一个和原数组等大的数组。<3>排序Array类还提供了QuickSort排序算法。使用Sort()方法可以对数组进行排序。但是使用Sort()方法需要实现IComparable接口(.Net已经为基本数据类型实现了IComparable接口,默认从小到大)。对于自定义类型,我们就必须实现IComparable<T>接口,这个接口只用一个方法CompareTo()。如果两者相等,就返回0。如果该实例在参数对象的前面,就返回小于0的值,反之就返回大于0的值。 我们也可以是通过实现IComparer<T>和IComparer接口。我们现在着重看看这个和IComparable接口的区别: ①IComparable在要比较对象的类中实现,可以比较该对象和另一个对象。②IComparer要在单独一个类中实现,可以比较任意两个对象。3、枚举 在foreach语句中使用枚举,可以迭代集合中的元素,而且不需要知道集合中的元素个数。foreach语句使用了一个枚举器,我们需要实现IEnumerable接口就可以使用foreach来迭代集合。(数组和集合已经默认实现了IEnumerable接口)。<1>foreach原理 和 IEnumerator 接口 foreach使用了IEnumerator接口的方法和属性。

//per为Person类的对象foreach(var p in per){Consle.WriteLine(p);}

C#编译器会将这段代码解析为

IEnumerator<Person> em = per.GetEnumerator();while(em.MoveNext()){Person p = em.Current;Console.WriteLine(p);}

IEnumerator接口的MoveNext()方法作用是:移动到集合的下一个元素,如果有则返回true,否则为false。Current属性为当前的值。

人,都有不能称心如意的时候,都有愿望落空的窘迫,

《C#高级编程》【第六章】数组

相关文章:

你感兴趣的文章:

标签云: