第5章 C#数组结构和枚举.docx
《第5章 C#数组结构和枚举.docx》由会员分享,可在线阅读,更多相关《第5章 C#数组结构和枚举.docx(24页珍藏版)》请在冰豆网上搜索。
第5章C#数组结构和枚举
第5章C#数组、结构和枚举
声明一个变量可以存储一个值,当遇到要存储多个相同类型的值的时候,变量就显得无能为力,数组正是在这种存储需求下设计的一种数据结构;常量可用来存储一个固定值,但是要存储多个固定值的时候,常量也失效了,这时候就要借助于枚举来实现;而结构是用来表示更加复杂的值类型,在结构里,用户可以声明不同数据类型的变量作为一个整体。
学完本章后,读者将了解数组、结构和枚举的用法。
本章主要涉及到的知识点有:
❑数组:
理解数组的概念,并创建数组。
❑使用数组:
包括定位、遍历、查找、排序等典型操作。
❑结构:
在需要考虑运行效率、且几乎不做运算的数据应该作为结构定义。
了解结构的用法。
❑枚举:
用于声明一组命名的常数。
理解枚举的好处,并使用枚举。
5.1C#中的数组
在日常生活中,人们用容器来存储物品,为了方便查找,总是将众多的物品分门别类地存储在不同的容器中。
在计算机程序中,同样会遇到处理大量具有相同类型的数据的时候,C#语言提供了“数组”这一数据结构,用于处理这样的数据。
5.1.1声明和初始化一维数组
【本节示例参考:
\源代码\chapter5\5.1.1\ArrayExample】
数组类似于生活中的容器,可以将一组数据类型相同的数据按照一定的顺序存储起来,存储在数组中的数据又叫元素,可以通过“索引”,或叫“下标”的整数来区分数组中的元素。
C#支持一维数组、多维数组(矩形数组)和数组的数组(交错数组)。
下面通过一个例子来学习一维数组。
暑期到了,音像店的老板Landy整理了一个CD架位置,并买了5张碟片放在上面出租,分别是《功夫熊猫》、《不可思议绿巨人》、《赤壁》、《木乃伊3》、《牛仔裤的夏天2》,有客人来租碟的时候,就从相应的位置找到这张碟。
如果把这个CD架理解为一个“数组”,它顺序存放的“元素”就是碟片。
在程序中,可以用以下的语句声明一个数组:
string[]movies;
声明以后,需要让计算机内存分配指定大小的空间,这叫初始化数组,如下所示:
movies=newstring[5];
用new关键字创建一个数组,实际上是在请求分配内存空间。
数组初始化成功后,就可以把元素存入数组,如下所示:
movies[0]=“功夫熊猫”;
movies[1]=“不可思议绿巨人”;
movies[2]=“赤壁”;
movies[3]=“木乃伊3”;
movies[4]=“牛仔裤的夏天2”;
注意:
紧跟数组名的数字,称之为“下标”或“索引”。
同C语言和大部分语言一样,C#数组的下标是从0开始,而不是从1。
如果要声明一个大小是5的数组,其下标就是从0~4。
通过以上内容,完成了数组的声明和初始化工作。
对于数组的声明和初始化,还可以用以下三种不同的方式简写。
(1)方式一:
string[]movies=newstring[5];
计算机内存将分配5个连续的存储string类型的空间。
因为字符串在C#中是引用类型,所以系统默认将每个元素初始化为NULL。
如果是数值型数组,将默认初始化为0,例如:
intnumArr=newint[5];//每个元素默认值为0
floatstrArr=newfloat[5];//每个元素默认值为0
objectobjArr=newobject[5];//每个元素默认值为NULL
(2)方式二:
string[]movies=newstring[]{“功夫熊猫”,”不可思议绿巨人”,”赤壁”,”木乃伊3”,”牛仔裤的夏天2”}
这里可以不用显式指定数组的大小,数组大小默认为初始化的元素个数。
(3)方式三:
如果在声明的时候就初始化,还可以简化为:
string[]movies={“功夫熊猫”,”不可思议绿巨人”,”赤壁”,”木乃伊3”,”牛仔裤的夏天2”}
以上的数组定义方式都能达到相同的目的,在定义数组的时候,之前如果不知道数组的元素,应采用方式一。
如果定义的时候就知道数组的元素,那么采取方式三会使程序更简洁。
数组定义好以后,就可以通过循环来访问数组的每一个元素:
for(inti=0;i{
Console.WriteLine("movies[{0}]={1}",i,movies[i]);
}
注意:
这里用到了数组的公共属性Length,返回数组的大小,其用法是:
数组.Length。
5.1.2声明和初始化其他类型的数组
通过一维数组的方式,解决了Landy保存部分碟片的问题。
但是Landy的烦恼是,随着碟片不断增多,查找碟片越来越难,如果能给每张碟片一个编号,编号表示碟片存放的位置在第几排第几列,这样会方便很多,每一张碟片的编号及其存储位置如图5.1所示。
11
12
13
14
15
21
…
31
41
图5.1碟片编号
现在用程序来实现。
编号是按照表格方式排列的,即是二维的,所以通过二维数组可以实现。
二维数组的声明方式如下:
1//方式一
2int[,]numbers;//声明
3numbers=newint[,]{//初始化
4{1,2,3,4,5},
5{1,2,3,4,5},
6{1,2,3,4,5},
7{1,2,3,4,5},
8{1,2,3,4,5}
9};
10//方式二
11int[,]numbers=newint[,]{//声明并初始化
12{1,2,3,4,5},
13{1,2,3,4,5},
14{1,2,3,4,5},
15{1,2,3,4,5},
16{1,2,3,4,5}
17};
声明的numbers数组可以存放5行5列的元素,即numbers.Length=25,要访问每个元素,可以通过下标,如numbers[0,0]=1。
以此类推,还可以创建更多维的数组。
此外,数组的元素可以是数组,即数组的数组(交错数组)。
交错数组的声明方式如下:
//方式一
int[][]jaggedArr=newint[2][];//声明交错数组
jaggedArr[0]=newint[5];//声明交错数组的第一个元素为一个大小为5的整型数组
jaggedArr[1]=newint[5];
//方式二
int[][]jaggedArr=newint[2][]{
newint[5],
newint[5]
};
综合一下,列出创建数组的惯用语法,如下所述:
❑data_type[]arr_name=newdata_type[intlength],这种方式定义一个元素数据类型为data_type,长度为length的一维数组arr_name。
❑data_type[]arr_name=newdata_type[]{item1,item2,…,itemn},这种方式定义一个元素数据类型为data_type,并通过“=”运算符进行赋值,其初始值为所给出的元素{item1,item2,…,itemn}的个数的一维数组。
❑data_type[,…,]arr_name=newdata_type[intlength1,intlength2,…,intlengthn],这种方式定义一个元素数据类型为data_type,秩为n,各维长度分别为length1,length2,…lengthn的多维数组arr_name。
❑data_type[][]…arr_name=newdata_type[intlength1][intlength2],这种方式定义一个交错数组arr_name,与定义多维数组非常相似,所不同的是,必须单独初始化交错数组每一维中的元素。
5.1.3支持数组语言实现的基类:
System.Array
【本节示例参考:
\源代码\chapter5\5.1.3\ArrayClass】
C#中的数组是由System.Array类派生而来的引用对象,它提供一些公共的属性和方法,对数组的操作提供了很大帮助。
其常用的属性和方法如表5.1所示(更多的属性和方法请参考MSDN)。
常用方法的使用会在以下章节中介绍。
表5.1Array类常用属性/方法说明
属性/方法
说明
Length
获得一个32位整数,表示Array的所有维数中元素的总数
IsFixedSize
返回一个布尔值,表示数组是否是固定大小的
IsReadOnly
返回一个布尔值,表示数组是否是只读的
Rank
获取Array的秩(维数),如二维数组numbers,numbers.Rank=2
BinarySearch
使用二进制搜索算法在一维的排序Array中搜索值
Clone
创建Array的浅表副本
Copy/CopyTo
将一个Array的一部分复制到另一个Array中
GetLength
获取一个32位整数,表示Array的指定维中的元素
GetLowerBound/GetUpperBound
获取Array的指定维度的下/上限
GetValue/SetValue
获取/设置Array中的指定元素值
IndexOf/LastIndexOf
返回一维Array或部分Array中某个值第一个/最后一个匹配项索引
Sort
对一维Array对象中的元素进行排序
下面的代码演示了System.Array类的属性的用法。
string[]movies={"功夫熊猫","不可思议绿巨人","赤壁","木乃伊3","牛仔裤的夏天2"};
Console.WriteLine("是否固定大小(IsFixedSize):
"+movies.IsFixedSize);
Console.WriteLine("是否只读(IsReadOnly):
"+movies.IsReadOnly);
Console.WriteLine("数组元素的总数(Length):
"+movies.Length);
Console.WriteLine("数组的秩(Rank):
"+movies.Rank);
Console.ReadLine();
输出的结果如下所示:
是否固定大小(IsFixedSize):
True
是否只读(IsReadOnly):
False
数组元素的总数(Length):
5
数组的秩(Rank):
1
5.1.4访问数组元素
【本节示例参考:
\源代码\chapter5\5.1.4\VisitExample】
Landy的新碟到了以后,生意好了很多,每天都有很多人去租碟。
通过每张碟的编号,从CD架上找到碟片。
这就好比从数组中去访问每个元素一样。
访问元素的方式很多,最直接的就是通过数组的下标。
假如所有碟片都放在一排陈列架上,就要用一维数组,例如:
定义一维数组:
string[]movies=newstring[]{“功夫熊猫”,”不可思议绿巨人”,”赤壁”,”木乃伊3”,”牛仔裤的夏天2”}
获取第一个元素:
movies[0]=“功夫熊猫”。
获取最后一个元素:
movies[4]=“牛仔裤的夏天2”。
假如碟片分别放在多排CD架上每排可以放多张碟,则要用二级数组,定义二维数组:
string[,]movies=newstring[,]{{“功夫熊猫”,”不可思议绿巨人”},{”赤壁”,”木乃伊3”}}
这样创建的数组,表示CD 在架上是这样摆放的,如图5.2所示。
图5.2CD架上的碟片
获取第一个元素:
movies[0,0]=“功夫熊猫”。
获取最后一个元素:
movies[1,1]=“木乃伊3”
注意:
如果引用的下标超出了数组的范围,会抛出System.IndexOutOfRangeException异常。
还可以通过System.Array类的GetValue/SetValue方法访问数组元素。
GetValue方法语法:
publicobjectGetValue(paramsint[]indices);,其中,多个int型参数indices的含义为下标。
方法返回一个object对象,这是C#中所有对象的基类,使用多态性,它可以指向所有的C#对象,如下所示:
1string[]movies=newstring[]{“功夫熊猫”,”不可思议绿巨人”,”赤壁”,”木乃伊3”,”钢铁侠”};
2for(inti=0;i3{
4Console.WriteLine(“碟片[{0}]={1}”,i,movies.GetValue(i));
5}
输出结果为:
碟片[0]=功夫熊猫
碟片[1]=不可思议绿巨人
碟片[2]=赤壁
碟片[3]=木乃伊3
碟片[4]=钢铁侠
SetValue方法语法为:
publicobjectSetValue(…);其作用是给指定下标的元素赋值,用法跟GetValue类似。
1string[]movies=newstring[]{“功夫熊猫”,”不可思议绿巨人”,”赤壁”,”木乃伊3”,”钢铁侠”};
2for(inti=movies.GetLowerBound(0);i<=movies.GetUpperBound(0);i++)
3{
4movies.SetValue("蝙蝠侠",i);
5Console.WriteLine(“碟片[{0}]={1}”,i,movies.GetValue(i));
6}
输出结果为:
碟片[0]=蝙蝠侠
碟片[1]=蝙蝠侠
碟片[2]=蝙蝠侠
碟片[3]=蝙蝠侠
碟片[4]=蝙蝠侠
注意:
SetValue(objectvalue,intindex),表示将value值赋给下标是index的元素;GetLowerBound方法可以获取数组某一维上的最低下标,而GetUpperBound则可获取其最高下标
5.1.5使用foreach语句遍历数组
【本节示例参考:
\源代码\chapter5\5.1.5\ForeachExample】
遍历是指全部访问数组中的元素一次且仅一次,可以在遍历的过程中完成查找等许多操作,需要注意一点:
foreach循环对数组内容进行只读访问,所以不能改变任何元素的值。
foreach语句格式如下:
foreach(data_typtitem_nameinarr_name)
{
//遍历每一个元素
}
注意:
无论是几维的数组,foreach语句都会从最深层的原子元素开始,遍历一次且仅一次,因此,不需要嵌套foreach循环。
代码5-1实现二维数组的遍历和元素的处理:
代码5-1利用foreach遍历数组示例:
ForeachExample.cs
1//定义二维数组
2int[,]numbers=newint[,]{
3{1,1,1,1,1},
4{2,2,2,2,2},
5{3,3,3,3,3},
6{4,4,4,4,4},
7{5,5,5,5,5}
8};
9//遍历-输出
10intcount=0;//定义一个计数器
11foreach(intnuminnumbers)
12{
13Console.Write(num+",");
14count++;//每输出一个元素,计数器加1
15if(count==5)//每输出5个元素
16{
17Console.WriteLine();//输出换行
18count=0;//初始化计数器
19}
20}
21Console.ReadLine();
输出结果:
1,1,1,1,1,
2,2,2,2,2,
3,3,3,3,3,
4,4,4,4,4,
5,5,5,5,5,
5.1.6查找数组元素
【本节示例参考:
\源代码\chapter5\5.1.6\SearchExample】
数组的元素是有序的,每一个元素对应一个唯一下标,在程序设计中,经常需要查找某个元素是否存在,以及该元素所在的位置等操作。
可以通过遍历整个数组的方式来查找,也可以通过System.Array提供的BinarySearch方法完成这些操作。
有个客户需要租《赤壁》,Landy需要在CD架中找到片名叫《赤壁》的碟片,下面的例子演示了查找元素《赤壁》所在的位置的两种方法:
1//定义数组
2string[]movies=newstring[]{"功夫熊猫","钢铁侠","赤壁","木乃伊3","牛仔裤的夏天2"};
3//方法一:
遍历,比较
4for(inti=0;i5{
6if(movies[i]=="赤壁")
7{
8Console.WriteLine("'赤壁'所在的位置是:
{0}",i);
9}
10}
11//方法二:
通过方法查找
12intindex=System.Array.BinarySearch(movies,"赤壁");
13Console.WriteLine("'赤壁'所在的位置是:
{0}",index);
输出的结果:
'赤壁'所在的位置是:
2
'赤壁'所在的位置是:
2
注意:
BinarySearch方法的第一个参数是需要查找的数组,第二个参数表示需要查找的元素,如果找到,方法返回该元素所在数组中的下标,如果没找到,将返回一个负数。
5.1.7对数组进行排序
【本节示例参考:
\源代码\chapter5\5.1.7\SortExample】
生活中会经常遇到一些排序问题,比如把一个班的考试成绩排序,足球比赛积分排序等。
那么怎样用计算机程序来完成排序工作呢?
这个问题前人早就有深刻的研究,总结出了很多高效率的排序算法,下面就来看看怎样用C#语言实现经典的冒泡排序。
在编写程序之前,先来了解一下冒泡排序算法原理。
假设现有5个数:
3,5,2,4,1,要将它们从小到大排序。
冒泡排序的过程如图5.3所示。
图5.3冒泡排序流程图
可以将每个元素比作一个气泡,排序的过程就是气泡不断向上冒的过程,越小的气泡冒得越高。
代码5-2用程序实现冒泡排序的功能。
代码5-2使用冒泡排序示例:
SortTest1.cs
1//定义数组
2int[]numbers={3,5,2,4,1};
3//输出排序前数组
4Console.WriteLine("排序前的数组:
");
5for(inti=0;i6{
7Console.Write(numbers[i]+",");
8}
9//开始排序
10for(inti=0;i11{
12//将最大的元素交换到最后
13for(intj=0;j14{
15if(numbers[j]>numbers[j+1])
16{
17//交换元素
18inttmp=numbers[j];
19numbers[j]=numbers[j+1];
20numbers[j+1]=tmp;
21}
22}
23}
24//输出排序后数组
25Console.WriteLine("\n排序后的数组:
");
26for(inti=0;i27{
28Console.Write(numbers[i]+",");
29}
30Console.ReadLine();
输出结果:
排序前的数组:
3,5,2,4,1,
排序后的数组:
1,2,3,4,5,
可以看到,对数组进行排序,是指按照一定的排序规则,如递增或递减规则,重新对数组中的所有元素排序。
除了冒泡排序等算法实现外,C#提供了很好的排序支持,可以使用Array类的Sort方法轻松完成这个功能。
代码5-3用程序实现冒泡排序。
代码5-3利用Sort排序数组示例:
SortTest2.cs
1//定义数组
2int[]numbers={3,5,2,4,1};
3//输出排序前数组
4Console.WriteLine("排序前的数组:
");
5for(inti=0;i6{
7Console.Write(numbers[i]+",");
8}
9//开始排序
10System.Array.Sort(numbers);
11//输出排序后数组
12Console.WriteLine("\n排序后的数组:
");
13for(inti=0;i14{
15Console.Write(numbers[i]+",");
16}
17Console.ReadLine();
该程序输出结果为:
1,1,1,1,1,
2,2,2,2,2,
3,3,3,3,3,
4,4,4,4,4,
5,5,5,5,5,
注意:
语法publicstaticvoidSort(Arrayarray);其中,参数array为待排序的数组。
5.2用C#中的结构来实现音像记录表
C#是面向对象的语言,结构可视为轻量级的类,是创建用于存储少量数据的数据类型的理想选择,不能表示以后可能要通过继承进行扩展的类型。
本章将通过讲解实现完善碟片编号的功能来向读者展示结构的用法。
5.2.1音像信息记录表程序实例
【本节示例参考:
\源代码\chapter5\5.2.1\StructTest】
通过数组一节,Landy完成了碟片的存储和给碟片编号的任务。
现在Landy希望把所有的碟片记录在电子表格中,以方便查找。
对电子表格的设计通常采用图5.4的形式。
图5.4碟片记录表
每一条记录,要求存储碟片的编号,名称,以及出租状态。
现在考虑如何用程序来实现:
碟片的编号用整数类型来表示,名称用字符串类型来表示,而出租状态可能是个方法。
显然用数组会有点力不从心,因为数组只能存储相同数据类型的数据。
这里需要一个更好的类型支持,这就是结构。
不管是用什么实现,下面迫不及待地想完成这张音像记录表的建立。
在理解相对晦涩的概念之前,笔者手把手教你完成这个项目,然后再来研究结构的语法和细节。