C#40图解教程.docx
- 文档编号:12265209
- 上传时间:2023-04-17
- 格式:DOCX
- 页数:46
- 大小:31.74KB
C#40图解教程.docx
《C#40图解教程.docx》由会员分享,可在线阅读,更多相关《C#40图解教程.docx(46页珍藏版)》请在冰豆网上搜索。
C#40图解教程
C#4.0图解教程
本章内容
..数组
..数组的类型
..数组是对象
..一维数组和矩形数组
..实例化一维数组或矩形数组
..访问数组元素
..初始化数组
..交错数组
..比较矩形数组和交错数组
..foreach语句
..数组协变
..数组继承的有用成员
..比较数组类型
14.1数组
数组实际上是由一个变量名称表示的一组同类型的数据元素。
每个元素通过变量名称和一个
或多个方括号中的索引名称来访问,如下所示:
14.1.1定义
让我们从C#中与数组有关的一些重要定义开始。
..元素:
数组的独立数据项被称作元素。
数组的所有元素必须是相同类型的,或继承自相
同的类型。
第14章
数组名索引
..秩/维度:
数组可以有任何为正数的维度数。
数组的维度数称作秩(rank)。
..维度长度:
数组的每一个维度有一个长度,就是这个方向的位置个数。
..数组长度:
数组的所有维度中的元素的总和称为数组的长度。
14.1.2重要细节
下面是有关C#数组的一些要点:
..数组一旦被创建,大小就固定了。
C#不支持动态数组。
..数组索引号是从0开始的。
也就是说,如果维度长度是n,索引号范围是从0到n.1。
例如,
图14-1演示了两个示例数组的维度和长度。
注意,对于每一个维度,索引范围从0到长度.1。
图14-1维度和大小
14.2数组的类型
C#提供了两种类型的数组:
..一维数组可以认为是单行元素或元素向量。
..多维数组是由主向量中的位置组成的,每一个位置本身又是一个数组,称为子数组
(sub-array)。
子数组向量中的位置本身又是一个子数组。
另外,有两种类型的多维度数组:
矩形数组(rectangulararray)和交错数组(jaggedarray),
它们有如下特性。
..矩形数组
..某个维度的所有子数组有相同长度的多维数组。
..不管有多少维度,总是使用一组方括号。
..交错数组
..每一个子数组都是独立数组的多维度数组。
..可以有不同长度的子数组。
..为数组的每一个维度使用一对方括号。
一维数组
二维数组
秩=1
数组长度=5
秩=2
数组长度=18
一组方括号
三组方括号
图14-2演示了C#中的各种数组。
图14-2一维、矩形以及交错数组
14.3数组是对象
数组实例是从System.Array继承的对象。
由于数组从BCL基类继承,它们也继承了很多有
用的方法,如下所示。
..Rank:
返回数组维度数的属性。
..Length:
返回数组长度(数组中所有元素的个数)的属性。
数组是引用类型,与所有引用类型一样,有数据的引用以及数据对象本身。
引用在栈或堆上,
并且数组对象本身总是在堆上。
图14-3演示了数组的内存配置和组成部分。
图14-3数组的结构
尽管数组总是引用类型,但是数组的元素可以是值类型或引用类型。
..如果存储的元素都是值类型,数组被称作值类型数组。
..如果存储在数组中的元素都是引用类型对象,数组被称作引用类型数组。
图14-4演示了值类型数组和引用类型数组。
一维
三维
交错数组
一维数组
矩形数组
交错数组
二维
从System.Array继
承的
属性或方法
内存
(栈或堆)
元素
堆
图14-4元素可以是值引用
14.4一维数组和矩形数组
一维数组和矩形数组的语法非常相似,因此我把它们放在了一起。
然后,我会单独介绍交错
数组。
声明一维数组或矩形数组
要声明一维数组或矩形数组,可以在类型和变量名称之间使用一对方括号。
方括号内的逗号就是秩说明符,它们指定了数组的维度数。
秩就是逗号数量加1。
比如,没
有逗号代表一维数组,一个逗号代表二维数组,依此类推。
基类和秩说明符构成了数组类型。
例如,如下代码行声明了long的一维数组。
数组类型是
long[],读作“long数组”。
如下代码展示了矩形数组声明的示例。
注意:
..可以使用任意多的秩说明符。
..不能在数组类型区域中放数组维度长度。
秩是数组类型的一部分,而维度长度不是类型
的一部分。
..数组声明后,维度数就是固定的了。
然而,维度长度直到数组实例化时才会被确定。
内存
堆
值
引用
秩说明符=1
数组类型
说明和C/C++不同,方括号在基类型后,而不在变量名称后。
14.5实例化一维数组或矩形数组
要实例化数组,我们可以使用数组创建表达式。
数组创建表达式由new运算符构成,后面是
基类名称和一对方括号。
方块号中以逗号分隔每一个维度的长度。
下面是一维数组声明的示例:
..arr2数组是四个int的一维数组。
..mcArr数组是4个MyClass引用的一维数组。
图14-5演示了它们在内存中的布局。
下面是矩形数组的示例,arr3数组是三维数组。
..数组长度是3*6*2=36。
..图14-5演示了它在内存中的布局。
秩说明符
不允许维度长度
数组类型
四个元素
数组创建表达式
维度长度
数组类型:
三维整型数组
数组类型:
二维整型数组
数组类型:
三维long数组
编译错误
图14-5声明和实例化数组
说明与对象创建表达式不一样,数组创建表达式不包含圆括号——即使是对于引用类型数
组。
14.6访问数组元素
在数组中使用整型值作为索引来访问数组元素。
..每一个维度的索引从0开始。
..方括号内的索引在数组名称之后。
如下代码给出了声明、写入、读取一维数组和二维数组的示例:
如下代码给出了一个创建并访问一维数组的完整过程:
这段代码产生了如下的输出:
堆
声明一维数组
向第3个元素写入值
从第3个元素读取值
声明二维数组
向数组写入值
向数组读取值
声明数组
实例化数组
设置值
读取并输出每个数组元素的值
14.7初始化数组
当数据被创建之后,每一个元素被自动初始化为类型的默认值。
对于预定义的类型,整型默
认值是0,浮点型的默认值为0.0,布尔型的默认值为false,而引用类型的默认值则是null。
例如,如下代码创建了数组并将它的4个元素的值初始化为0。
图14-6演示了内存中的布局。
图14-6一维数组的自动初始化
14.7.1显式初始化一维数组
对于一维数组,我们可以通过在数组实例化的数组创建表达式之后包括初始化列表来设置显
式初始值。
..初始值必须以逗号分隔,并封闭在一组花括号内。
..维度长度是可选的,因为编译器可以通过初始化值的个数来推断长度。
..注意,在数组创建表达式和初始化列表中间没有分隔符。
也就是说,没有等号或其他连
接运算符。
例如,下面的代码创建了一个数组,并将它的4个元素初始化为花括号内的值。
图14-7演示
了内存中的布局。
图14-7一维数组的显式初始化
内存
内存
没有连接运算符
初始化列表
14.7.2显式初始化矩形数组
要显式初始化矩形数组:
..每一个初始值向量必须封闭在花括号内。
..除了初始值,每一个维度的初始化列表和组成部分必须使用逗号分隔。
例如,如下代码演示了具有初始化列表的二维数组的声明。
图14-8演示了在内存中的布局。
图14-8初始化矩形数组
14.7.3初始化矩形数组的语法点
矩形数组使用嵌套的、逗号分隔的初始化列表进行初始化。
初始化列表嵌套在花括号内。
有
时会混淆,因此,对于嵌套、分组和逗号的正确使用,如下技巧可能会有用:
..逗号用作元素和分组之间的分隔符。
..逗号不在左花括号之间使用。
..逗号不在右花括号之前使用。
..从左向右读佚指示符,指定最后一个数字作为“元素”,其他数字作为“分组”。
例如,下面的声明可以读作:
“intArray有四组两个元素一组的分组三组。
”
14.7.4快捷语法
在语句中结合声明、数组创建和初始化时,我们可以省略语法的数组创建表达式部分。
快捷
语法如图14-9所示。
初始化列表由逗号分隔
由逗号分隔的嵌套初始化列表
内存
图14-9声明、创建以及初始化数组的快捷语法
14.7.5隐式类型数组
直到现在,我们一直都在数组声明的开始处显式指定数组类型。
然而,在C#3.0中,和其他
局部变量一样,数组可以是隐式类型的。
也就是说:
..当初始化数组时,我们可以让编译器根据初始化器的类型来推断数组类型。
只要所有初
始化器能隐式转换为单个类型,就可以这么做。
..和隐式类型的局部变量一样,使用var关键字来替代数组类型。
如下代码演示了三组数组的声明的显式版本和隐式版本。
第一组是一维int数组。
第二组是
二维int数组。
第三组是字符串数组。
注意,在隐式类型intArr4的声明中,我们仍然需要在初
始化中提供佚说明符。
14.7.6综合内容
如下代码把我们迄今学到的知识点放在了一起。
它创建、初始化并使用了一个矩形数组。
这段代码产生了如下的输出:
等价
等价
显式
显式
关键字
推断
秩说明符
声明、创建和初始化一个隐式类型的数组
输出值
14.8交错数组
交错数组是数组的数组。
与矩形数组不同,交错数组的子数组可以有不同数目的元素。
例如,如下代码声明了一个二维交错数组。
图14-10演示了数组在内存中的布局。
..第一个维度的长度是3。
..声明可以读作“jagArr是三个int数组的数组”。
..注意,图中有四个数组对象——其中一个针对顶层数组,另外三个针对子数组。
图14-10交错数组是数组的数组
14.8.1声明交错数组
交错数组的声明语法要求每一个维度都有一对独立的方括号。
数组变量声明中的方括号数决
定了数组的佚。
..交错数组可以有任何大于1的维度。
..和矩形数组一样,维度长度不能包括在数组类型的声明部分中。
jagArr[3][]是具有三个数组的数
秩说明符
声明并创建顶层数组
声明并创建子数组
内存
堆
秩等于2
14.8.2快捷实例化
我们可以将用数组创建表达式创建的最高级别数组和交错数组的声明相结合,如下面的声明
所示。
结果如图14-11所示。
图14-11快捷最高级别实例化
不能在声明语句中初始化最高级别数组之外的数组。
14.8.3实例化交错数组
和其他类型的数组不一样,交错数组的完全初始化不能在一个步骤中完成。
由于交错数组是
独立数组的数组——每一个数组必须独立创建。
实例化完整的交错数组需要如下步骤:
(1)首先,实例化顶层数组。
(2)其次,分别实例化每一个子数组,把新建数组的引用赋值给包含它们的数组的合适元
素。
例如,如下代码演示了二维交错数组的声明、实例化和初始化。
注意,在代码中,每一个子
数组的引用都赋值给了顶层数组的元素。
步骤1到步骤4与图14-12中被编号的表示相对应。
数组类型
数组名
三个子数组
允许
不允许
内存
堆
秩等于3
编译错误
实例化顶层数组
实例化子数组
实例化子数组
实例化子数组
图14-12创建一个二维交错数组
14.8.4交错数组中的子数组
由于交错数组中的子数组本身就是数组,因此交错数组中也可能有矩形数组。
例如,如下代
码创建了一个有三个二维矩形数组的交错数组,并将它们初始化,然后显示了它们的值。
图14-13演示了结构。
代码使用了数组的继承自System.Array的GetLength(intn)方法来获取数组中指定维
度的长度。
内存
内存
堆
堆
堆
堆
获取Arr维度0的长度
获取Arr[i]维度0的长度
获取Arr[i]维度1的长度
带有二维数组的交错数组
实例化带有三个二维数组的交错数组
图14-13三个二维数组构成的交错数组
14.9比较矩形数组和交错数组
矩形数组和交错数组的结构的区别非常大。
例如,图14-14演示了3×3的矩形数组以及一个
由三个长度为3的一维数组构成的交错数组的结构。
..两个数组都保存了9个整数,但是它们的结构却很不相同。
..矩形数组只有单个数组对象,而交错数组有4个数组对象。
图14-14比较矩形数组和交错数组的结构
在CIL中,一维数组有特定的指令用于性能优化。
矩形数组没有这些指令,并且不在相同级
别进行优化。
因此,有时使用一维数组(可以被优化)的交错数组相比矩形数组会更有效率。
另一方面,矩形数组的编程复杂度更小,因为它会被作为一个单元而不是数组的数组。
14.10foreach语句
foreach语句允许我们连续访问数组中的每一个元素。
其实它是一个比较普遍的结构,因为可
以和其他集合类型一起使用——但是在这部分内容中,我们只会讨论它和数组的使用,第20章会
介绍它和其他集合类型的使用。
有关foreach的重点如下所示。
堆
堆
3×3矩形数组
一个数组对象
没有优化
4个数组对象
更复杂
优化后的数组(一维)
3×3交错数组
..迭代变量是临时的,只读的,并且和数组中元素的类型相同。
foreach语句使用迭代变
量来连续表示数组中的每一个元素。
..foreach语句的语法如下,其中:
..Type是数组中元素的类型。
我们可以显式提供它的类型,或者,从C#3.0开始,也可以
隐式提供它的类型并通过编译器来推断,因为编译器知道数组的类型。
..标识符是迭代变量的名字。
..数组名称是要处理的数组的名字。
..语句是要为数组中的每一个元素执行一次的单条语句或语句块。
在之后的内容中,有时会使用隐式类型,而有时又会使用显式类型,这样我们就可以看到使
用的确切类型,但是两种形式的语法是等价的。
foreach语句以如下方式工作:
..从数组的第一个元素开始并把它赋值给迭代变量。
..它然后执行语句主体。
在主体中,我们可以把迭代变量作为数组元素的只读别名。
..在主体执行之后,foreach语句选择数组中的下一个元素并重复处理。
这样,它就循环遍历了数组,允许我们逐个访问每一个元素。
例如,如下代码演示了foreach
语句和一个具有4个整数的一维数组的使用:
..foreach语句的主体WriteLine为数组的每一个元素执行一次。
..第一次遍历时,迭代变量item就有数组的第一个元素的值。
每次成功执行后,它就有了
数组中下一个元素的值。
14.10.1迭代变量是只读的
由于迭代变量的值是只读的,所以它不能被改变。
但是,对于值类型数组和引用类型数组而
言效果不一样。
对于值类型数组,我们不能改变数组的数据。
例如,在如下的代码中,尝试改变迭代变量中
显示类型迭代变量声明
隐式类型迭代变量声明
迭代变量声明
使用迭代变量
的数据产生了编译时错误消息:
对于引用类型,我们仍然不能改变迭代变量,但是迭代变量只是保存了数据的引用,而不是
数据本身。
因此,我们可以通过迭代变量改变数据。
如下代码创建了一个有4个MyClass对象的数组并将其初始化。
在第一个foreach语句中,
每一个对象中的数据被改变。
在第二个foreach语句中,从对象读取改变后的值。
这段代码产生了如下的输出:
14.10.2foreach语句和多维数组
在多维数组中,元素的处理次序是最右边的索引号最先递增。
当索引从0到长度减1时,下一
个左边的索引被递增,右边的索引被重置成0。
1.矩形数组的示例
如下代码演示了foreach语句用于矩形数组:
创建数组
创建类对象
设置字段
改变数据
读取改变的数据
这段代码产生了如下的输出:
2.交错数组的示例
一个交错数组是数组的数组,我们必须为交错数组中的每一个维度使用独立的foreach语
句。
foreach语句必须嵌套以确保每一个嵌套数组都被正确处理。
例如,在如下代码中,第一个foreach语句遍历了顶层数组(arr1)选择了下一个要处理
的子数组。
内部的foreach语句处理了子数组的每一个元素。
这段代码产生了如下的输出:
14.11数组协变
在某些情况下,即使某个对象不是数组的基类型,我们也可以把它赋值给数组元素。
这种属
性叫做协变(covariance)。
在下面的情况下可以使用协变:
..数组是引用类型数组。
..在赋值的对象类型和数组基类型之间有隐式转换或显式转换。
由于在派生类和基类之间总是有隐式转换的,因此总是可以将一个派生类的对象赋值给为基
类声明的数组。
例如,如下代码声明了两个类,A和B,B类从继承自A类。
最后一行展示了把类型B的对象赋
值给类型A的数组元素而产生的协变。
图14-15演示了代码的内存布局。
图14-15数组出现协变
A[]类型的两个数
组
声明的A类型的对象
B类型对象也可以被接
受,因为它从A继承
堆
处理顶层数组
处理第二层数组
说明值类型数组没有协变。
14.12数组继承的有用成员
我之前提到过,C#数组从System.Array类继承。
它们可以从基类继承很多有用的属性和方
法,表14-1列出了其中最有用的一些。
表14-1数组继承的一些有用成员
成员
类型
生存期
意义
Rank
属性
实例
获取数组的维度数
Length
属性
实例
获取数组中所有维度的元素总和
GetLength
方法
实例
返回数组的指定维度的长度
Clear
方法
静态
设置元素的范围为0或null
Sort
方法
静态
在一维数组中对元素进行排序
BinarySearch
方法
静态
使用二进制搜索,搜索一维数组中的值
Clone
方法
实例
进行数组的浅复制——复制值类型数组和引用类型数组的元素
IndexOf
方法
静态
返回一维数组中遇到的第一个值
Reverse
方法
静态
将一维数组中的某一范围内的元素顺序倒过来
GerUpperBound
方法
实例
获取指定维度的上限
例如,下面的代码使用了其中的一些属性和方法:
基类
派生类
两个A[]类型的数组
普通:
将A类型的对象赋值给A类型的数组
协变:
将B类型的对象赋值给A类型的数组
这段代码产生了如下的输出:
Clone方法
Clone方法为数组进行浅复制。
也就是说,它只创建了数组本身的克隆。
如果是引用类型数组,
它不会复制元素引用的对象。
对于值类型数组和引用类型数组而言,有不同的结果。
..克隆值类型数组会产生两个独立数组。
..克隆引用类型数组会产生指向相同对象的两个数组。
Clone方法返回object类型的引用,它必须被强制转换成数组类型。
例如,如下代码给出了一个克隆值类型数组的示例,它产生了两个独立的数组。
图14-16演
示了代码中的一些步骤。
数组类型
返回
bjt
图14-16克隆值类型数组产生了两个独立数组
克隆引用类型数组会产生指向相同对象的两个数组,如下代码给出了这个示例。
图14-17演
示了代码中的一些步骤。
图14-17克隆引用类型数组产生了引用相同对象的两个数组
堆
堆
堆
堆
堆
步骤1
步骤2
步骤3
14.13比较数组类型
表14-2总结了三种类型的数组的重要相似点和不同点。
表14-2比较数组类型的总结
数组类型
数组对象
语法
形状
结构
逗号
一维
1
单组
没有
一维int[3]
..在CIL中优化指令
矩形
..多维度
1
单组
有
二维int[3,6]
..多维数组中的子数组
必须是相同长度的
三维
int[3,6,2]
交错
..多维度
..子数组可以有不同长
度
多个
多组
没有
交错int[4][]
步骤1
步骤2
步骤3
枚举数和迭代器
本章内容
..枚举数和可枚举类型
..使用IEnumerator接口
..IEnumerable接口
..不实现接口的枚举数
..泛型枚举接口
..IEnumerator
..IEnumerable
..迭代器
..常见迭代器模式
..产生可枚举类型和枚举数
..产生多个可枚举类型
..产生多个枚举数
..迭代器实质
20.1枚举数和可枚举类型
在第14章中,我们已经知道可以使用foreach语句来遍历数组中的元素。
在本章中,我们会
进一步探讨数组,来看看为什么它们可以被foreach语句处理。
我们还会研究如何为用户自定义
的类增加这个功能。
在本章的后面部分,我们会讨论迭代器的使用。
20.1.1使用foreach语句
当我们为数组使用foreach语句时,这个语句为我们依次取出了数组中的每一个元素,允许
我们读取它的值。
例如,如下的代码声明了一个有4个元素的数组,然后使用foreach来循环打印这些项的值:
第20章
这段代码产生了如下的输出:
为什么数组可以这么做?
看上去很神奇。
原因是数组可以按需提供一个叫做枚举数(enumerator)
的对象。
枚举数可以依次返回请求的数组的元素。
枚举数“知道”项的次序并且跟踪它
在序列中的位置,然后返回请求的当前项。
对于有枚举数的类型而言,必须有一个方法来获取它们。
在.NET中获取一个对象枚举数的
标准方法是调用对象的GetEnumerator方法。
实现GetEnumerator方法的类型叫做可枚举类型
(enumerabletype或enumerable)。
数组是可枚举类型。
图20-1演示了可枚举类型和枚举数之间的关系。
图20-1枚举数和可枚举类型概览
foreach结构被设计用来和可枚举类型一起使用。
只要给它的遍历对象是
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 40 图解 教程