合集5数组.docx

上传人:b****5 文档编号:7458249 上传时间:2023-01-24 格式:DOCX 页数:24 大小:57.43KB
下载 相关 举报
合集5数组.docx_第1页
第1页 / 共24页
合集5数组.docx_第2页
第2页 / 共24页
合集5数组.docx_第3页
第3页 / 共24页
合集5数组.docx_第4页
第4页 / 共24页
合集5数组.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

合集5数组.docx

《合集5数组.docx》由会员分享,可在线阅读,更多相关《合集5数组.docx(24页珍藏版)》请在冰豆网上搜索。

合集5数组.docx

合集5数组

目录

第5章数   组2

5.1 简单数组2

5.1.1 数组的声明2

5.1.2 数组的初始化2

5.1.3 访问数组元素3

5.1.4 使用引用类型3

5.2 多维数组4

5.3 锯齿数组5

5.4 Array类6

5.4.1 属性6

5.4.2 创建数组7

5.4.3 复制数组7

5.4.4 排序9

5.5 数组和集合接口11

5.5.1 IEumerable接口11

5.5.2 ICollection接口11

5.5.3 IList接口11

5.6 枚举12

5.6.1 IEnumerator接口12

5.6.2 foreach语句13

5.6.3 yield语句13

5.7 小结17

第5章数   组

如果需要使用同一类型的多个对象,就可以使用集合和数组。

C#用特殊的记号声明和使用数组。

Array类在后台发挥作用,为数组中元素的排序和过滤提供了几个方法。

使用枚举器,可以迭代数组中的所有元素。

本章讨论如下内容:

● 简单数组

● 多维数组

● 锯齿数组

● Array类

● 数组的接口

● 枚举

5.1 简单数组

如果需要使用同一类型的多个对象,就可以使用数组。

数组是一种数据结构,可以包含同一类型的多个元素。

5.1.1 数组的声明

在声明数组时,应先定义数组中元素的类型,其后是一个空方括号和一个变量名。

例如,下面声明了一个包含整型元素的数组:

int[]myArray;

5.1.2 数组的初始化

声明了数组后,就必须为数组分配内存,以保存数组的所有元素。

数组是引用类型,所以必须给它分配堆上的内存。

为此,应使用new运算符,指定数组中元素的类型和数量来初始化数组的变量。

下面指定了数组的大小。

提示:

值类型和引用类型请参见第3章。

myArray=newint[4];

在声明和初始化后,变量myArray就引用了4个整型值,它们位于托管堆上,如图5-1所示。

 

图 5-1

警告:

在指定了数组的大小后,如果不复制数组中的所有元素,就不能重新设置数组的大小。

如果事先不知道数组中应包含多少个元素,就可以使用集合。

集合请参见第10章。

除了在两个语句中声明和初始化数组之外,还可以在一个语句中声明和初始化数组:

int[]myArray=newint[4];

还可以使用数组初始化器为数组的每个元素赋值。

数组初始化器只能在声明数组变量时使用,不能在声明数组之后使用。

int[]myArray=newint[4]{4,7,11,2};

如果用花括号初始化数组,还可以不指定数组的大小,因为编译器会计算出元素的个数:

int[]myArray=newint[]{4,7,11,2};

使用C#编译器还有一种更简化的形式。

使用花括号可以同时声明和初始化数组,编译器生成的代码与前面的例子相同:

int[]myArray={4,7,11,2};

5.1.3 访问数组元素

数组在声明和初始化后,就可以使用索引器访问其中的元素了。

数组只支持有整型参数的索引器。

提示:

在定制的类中,可以创建支持其他类型的索引器。

创建定制索引器的内容请参见第6章。

通过索引器传送元素号,就可以访问数组。

索引器总是以0开头,表示第一个元素。

可以传送给索引器的最大值是元素个数减1,因为索引从0开始。

在下面的例子中,数组myArray用4个整型值声明和初始化。

用索引器0、1、2、3就可以访问该数组中的元素。

int[]myArray=newint[]{4,7,11,2};

intv1=myArray[0];   //readfirstelement

intv2=myArray[1];   //readsecondelement

myArray[3]=44;     //changefourthelement

警告:

如果使用错误的索引器值(不存在对应的元素),就会抛出IndexOutOfRangeException类型的异常。

如果不知道数组中的元素个数,则可以在for语句中使用Length属性:

for(inti=0;i

{

Console.WriteLine(myArray[i]);

}

除了使用for语句迭代数组中的所有元素之外,还可以使用foreach语句:

for(intvalinmyArray)

{

Console.WriteLine(val);

}

提示:

foreach语句利用了本章后面讨论的IEnumerable和IEnumerator接口。

5.1.4 使用引用类型

不但能声明预定义类型的数组,还可以声明定制类型的数组。

下面用Person类来说明,这个类有两个构造函数、自动实现的属性Firstname和Lastname、以及ToString()方法的一个重写:

publicclassPerson

{

publicPerson()

}

{

publicPerson(stringfirstName,stringlastName)

{

this.firstname=firstName;

this.lastname=lastName;

}

publicstringFirstname{get;set;}

publicstringLastname{get;set;}

publicoverridestringToString()

{

returnString.Format("{0}{1}",firstName,lastName);

}

}

声明一个包含两个Person元素的数组,与声明一个int数组类似:

Person[]myPersons=newPerson[2];

但是必须注意,如果数组中的元素是引用类型,就必须为每个数组元素分配内存。

若使用了数组中未分配内存的元素,就会抛出NullReferenceException类型的异常。

提示:

第14章介绍了错误和异常的详细内容。

使用从0开始的索引器,可以为数组的每个元素分配内存:

myPersons[0]=newPerson("Ayrton","Senna");

myPersons[1]=newPerson("Michael","Schumacher");

图5-2显示了Person数组中的对象在托管堆中的情况。

myPersons是一个存储在堆栈上的变量,该变量引用了存储在托管堆上的Person元素数组。

这个数组有足够容纳两个引用的空间。

数组中的每一项都引用了一个Person对象,而这些Person对象也存储在托管堆上。

 

图 5-2

与int类型一样,也可以对定制类型使用数组初始化器:

Person[]myPersons={newPerson("Ayrton","Senna"),

newPerson("Michael","Schumacher")};

5.2 多维数组

一般数组(也称为一维数组)用一个整数来索引。

多维数组用两个或多个整数来索引。

图5-3是二维数组的数学记号,该数组有三行三列。

第一行的值是1、2和3,第三行的值是7、8和9。

 

图 5-3

在C#中声明这个二维数组,需要在括号中加上一个逗号。

数组在初始化时应指定每一维的大小(也称为阶)。

接着,就可以使用两个整数作为索引器,来访问数组中的元素了:

int[,]twodim=newint[3,3];

twodim[0,0]=1;

twodim[0,1]=2;

twodim[0,2]=3;

twodim[1,0]=4;

twodim[1,1]=5;

twodim[1,2]=6;

twodim[2,0]=7;

twodim[2,1]=8;

twodim[2,2]=9;

提示:

数组声明之后,就不能修改其阶数了。

如果事先知道元素的值,也可以使用数组索引器来初始化二维数组。

在初始化数组时,使用一个外层的花括号,每一行用包含在外层花括号中的内层花括号来初始化。

int[,]twodim={

{1,2,3},

{4,5,6},

{7,8,9},

};

提示:

使用数组初始化器时,必须初始化数组的每个元素,不能遗漏任何元素。

在花括号中使用两个逗号,就可以声明一个三维数组:

int[,,]threedim={

{{1,2},{3,4}},

{{5,6},{7,8}},

{{9,10},{11,12}},

};

Console.WriteLine(threedim[0,1,1]);

5.3 锯齿数组

二维数组的大小是矩形的,例如3×3个元素。

而锯齿数组的大小设置是比较灵活的,在锯齿数组中,每一行都可以有不同的大小。

图5-4比较了有3×3个元素的二维数组和锯齿数组。

图中的锯齿数组有3行,第一行有2个元素,第二行有6个元素,第三行有3个元素。

 

图 5-4

在声明锯齿数组时,要依次放置开闭括号。

在初始化锯齿数组时,先设置该数组包含的行数。

定义各行中元素个数的第二个括号设置为空,因为这类数组的每一行包含不同的元素数。

之后,为每一行指定行中的元素个数:

int[][]jagged=newint[3][];

jagged[0]=newint[2]{1,2};

jagged[1]=newint[6]{3,4,5,6,7,8};

jagged[2]=newint[3]{9,10,11};

迭代锯齿数组中所有元素的代码可以放在嵌套的for循环中。

在外层的for循环中,迭代每一行,内层的for循环迭代一行中的每个元素:

for(introw=0;row

{

for(intelement=0;element

{

Console.WriteLine("row:

{0},element:

[1],value:

{2}",

row,element,

}

}

该迭代显示了所有的行和每一行中的各个元素:

row:

0,element:

0,value:

1

row:

0,element:

1,value:

2

row:

1,element:

0,value:

3

row:

1,element:

1,value:

4

row:

1,element:

2,value:

5

row:

1,element:

3,value:

6

row:

1,element:

4,value:

7

row:

1,element:

5,value:

8

row:

2,element:

1,value:

9

row:

2,element:

2,value:

10

row:

2,element:

3,value:

11

5.4 Array类

用括号声明数组是C#中使用Array类的记号。

在后台使用C#语法,会创建一个派生于抽象基类Array的新类。

这样,就可以使用Array类为每个C#数组定义的方法和属性了。

例如,前面就使用了Length属性,还使用foreach语句迭代数组。

其实这是使用了Array类中的GetEnumerator()方法。

5.4.1 属性

Array类包含的如下属性可以用于每个数组实例。

本章后面还将讨论其他更多的属性。

表 5-1

属性

说明

Length

Length属性返回数组中的元素个数。

如果是一个多维数组,该属性会返回所有阶的元素个数。

如果需要确定一维中的元素个数,则可以使用GetLength()方法

LongLength

Length属性返回int值,而LongLength属性返回long值。

如果数组包含的元素个数超出了32位int值的取值范围,就需要使用LongLength属性,来获得元素个数

Rank

使用Rank属性可以获得数组的维数

5.4.2 创建数组

Array类是一个抽象类,所以不能使用构造函数来创建数组。

但除了可以使用C#语法创建数组实例之外,还可以使用静态方法CreateInstance()创建数组。

如果事先不知道元素的类型,就可以使用该静态方法,因为类型可以作为Type对象传送给CreateInstance()方法。

下面的例子说明了如何创建类型为int、大小为5的数组。

CreateInstance()方法的第一个参数应是元素的类型,第二个参数定义数组的大小。

可以用SetValue()方法设置值,用GetValue()方法读取值:

ArrayintArray1=Array.CreateInstance(typeof(int),5);

for(inti=0;i<5;i++)

{

intArray1.SetValue(33,i);

}

for(inti=0;i<5;i++)

{

Console.WriteLine(intArray1.GetValue(i));

}

还可以将已创建的数组强制转换成声明为int[]的数组:

int[]intArray2=(int[])intArray1;

CreateInstance()方法有许多重载版本,可以创建多维数组和不基于0的数组。

下面的例子就创建了一个包含2×3个元素的二维数组。

第一维基于1,第二维基于0:

int[]lengths={2,3};

int[]lowerBounds={1,10};

Arrayracers=Array.CreateInstance(typeof(Person),lengths,lowerBounds);

SetValue()方法设置数组的元素,其参数是每一维的索引:

racers.SetValue(newPerson("Alain","Prost"),1,10);

racers.SetValue(newPerson("Emerson","Fittipaldi"),1,11);

racers.SetValue(newPerson("Ayrton","Senna"),1,12);

racers.SetValue(newPerson("Ralf","Schumacher"),2,10);

racers.SetValue(newPerson("Fernando","Alonso"),2,11);

racers.SetValue(newPerson("Jenson","Button"),2,12);

尽管数组不是基于0的,但可以用一般的C#记号将它赋予一个变量。

只需注意不要超出边界即可:

Person[,]racers2=(Person[,])racers;

Personfirst=racers2[1,10];

Personlast=racers2[2,12];

5.4.3 复制数组

因为数组是引用类型,所以将一个数组变量赋予另一个数组变量,就会得到两个指向同一数组的变量。

而复制数组,会使数组实现ICloneable接口。

这个接口定义的Clone()方法会创建数组的浅副本。

如果数组的元素是值类型,就会复制所有的值,如图5-5所示:

intintArray1={1,2};

intintArray2=(int[])intArray1.Clone();

 

图 5-5

如果数组包含引用类型,则不复制元素,而只复制引用。

图5-6显示了变量beatles和beatlesClone,其中beatlesClone是通过在beatles上调用Clone()方法来创建的。

beatles和beatlesClone引用的Person对象是相同的。

如果修改beatlesClone中一个元素的属性,就会改变beatles中的对应对象。

Person[]beatles={

newPerson("John","Lennon"),

newPerson("Paul","McCartney"),

};

Person[]beatlesClone=(Person[])beatles.Clone();

 

图 5-6

除了使用Clone()方法之外,还可以使用Array.Copy()方法创建浅副本。

但Clone()方法和Copy()方法有一个重要区别:

Clone()方法会创建一个新数组,而Copy()方法只是传送了阶数相同、有足够元素空间的已有数组。

提示:

如果需要包含引用类型的数组的深副本,就必须迭代数组,创建新对象。

5.4.4 排序

Array类实现了对数组中元素的冒泡排序。

Sort()方法需要数组中的元素实现IComparable接口。

简单类型,如System.String和System.Int32实现了IComparable接口,所以可以对包含这些类型的元素排序。

在示例程序中,数组name包含string类型的元素,这个数组是可以排序的。

String[]names={

"ChristinaAguillera",

"Shakira",

"Beyonce",

"GwenStefani"

};

Array.Sort(names);

foreach(stringnameinnames)

{

Console.WriteLine(name);

}

该应用程序的输出是排好序的数组:

Beyonce

ChristinaAguillera

GwenStefani

Shakira

如果

修改Person类,使之执行IComparable接口。

对LastName的值进行比较。

LastName是string类型,而String类已经实现了IComparable接口,所以可以使用String类中CompareTo()方法的实现代码。

如果LastName的值相同,就比较FirstName:

publicclassPerson:

IComparable

{

publicintCompareTo(objectobj)

{

Personother=objasPerson;

intresult=this.LastName.CompareTo(

other.LastName);

if(result==0)

{

result=this.FirstName.CompareTo(

other.FirstName);

}

returnresult;

}

//…

现在可以按照姓氏对Person对象数组排序了:

Person[]persons={

newPerson("Emerson","Fittipaldi"),

newPerson("Niki","Lauda"),

newPerson("Ayrton","Senna"),

newPerson("Michael","Schumacher"),

};

Array.Sort(persons);

foreach(Personpinpersons)

{

Console.WriteLine(p);

}

使用Person类的排序功能,会得到按姓氏排序的姓名:

EmersonFittipaldi

NikiLauda

MichaelSchumacher

AyrtonSenna

如果Person对象的排序方式与上述不同,或者不能修改在数组中用作元素的类,就可以执行IComparer接口。

这个接口定义了方法Compare()。

IComparable接口必须由要比较的类来执行,而IComparer接口独立于要比较的类。

这就是Compare()方法定义了两个要比较的变元的原因。

其返回值与IComparable接口的CompareTo()方法类似。

类PersonComparer实现了IComparer接口,可以按照firstName或lastName对Person对象排序。

枚举PersonCompareType定义了与PersonComparer相当的排序选项:

FirstName和LastName。

排序的方式由类PersonComparer的构造函数定义,在该构造函数中设置了一个PersonCompareType值。

Compare()方法用一个switch语句指定是按firstName还是lastName排序。

publicclassPersonComparer:

IComparer

{

publicenumPersonCompareType

{

FirstName,

LastName

}

 privatePersonCompareTypecompareType;

 publicPersonComparer(PersonCompareTypecompareType)

{

this.compareType=compareType;

}

publicintCompare(objectx,objecty)

{

Personp1=xasPerson;

Personp1=yasPerson;

Switch(compareType)

{

casePersonCompareType.FirstName:

returnp1.FirstName.CompareTo(p2.FirstName);

casePersonCompareType.LastName:

returnp1.LastName.CompareTo(p2.LastName);

default:

thrownewArgumentException("unexpextedcomparetype")

}

}

}

现在,可以将一个PersonComparer对象传送给Array.Sort()方法的第二个变元。

下面是按名字对persons数组排序:

Array.Sort(persons,

newPersonComparer(PersonComparer.PersonCompareType.FirstName));

foreach(Personpinpersons)

{

Console.WriteLine(p);

}

persons数组现在按名字排序:

AyrtonSenna

EmersonFittipaldi

MichaelSchumacher

NikiLauda

提示:

Array类还提供了Sort方法,它需要将一个委托作为变元。

第7章将介绍如何使用委托。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 电力水利

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1