数组、集合的接口和枚举介绍
2011-01-05 17:00
239 查看
5.5
数组和集合接口
Array类实现了IEumerable、ICollection和IList接口,以访问和枚举数组中的元素。由于用定制数组创建的类派生于Array抽象类,所以能使用通过数组变量执行的接口中的方法和属性。5.5.1 IEumerable接口
IEumerable是由foreach语句用于迭代数组的接口。这是一个非常特殊的特性,在下一节中讨论。5.5.2 ICollection接口
ICollection接口派生于IEumerable接口,并添加了如表5-2所示的属性和方法。这个接口主要用于确定集合中的元素个数,或用于同步。表 5-2
ICollection接口的属性和方法 | 说 明 |
Count | Count属性可确定集合中的元素个数,它返回的值与Length属性相同 |
IsSynchronized SyncRoot | IsSynchronized属性确定集合是否是线程安全的。对于数组,这个属性总是返回false。对于同步访问,SyncRoot属性可以用于线程安全的访问。第19章介绍了线程和同步,探讨了如何用集合实现线程安全性 |
CopyTo() | 利用CopyTo()方法可以将数组的元素复制到现有的数组中。它类似于静态方法Array.Copy() |
5.5.3 IList接口
IList接口派生于ICollection接口,并添加了下面的属性和方法。Array类实现IList接口的主要原因是,IList接口定义了Item属性,以使用索引器访问元素。IList接口的许多其他成员是通过Array类抛出NotSupportedException异常实现的,因为这些不应用于数组。IList接口的所有属性和方法如表5-3所示。表 5-3
IList 接 口 | 说 明 |
Add() | Add()方法用于在集合中添加元素。对于数组,该方法会抛出NotSupportedException异常 |
Clear() | Clear()方法可清除数组中的所有元素。值类型设置为0,引用类型设置为null |
Contains() | Contains()方法可以确定某个元素是否在数组中。其返回值是true或false。这个方法会对数组中的所有元素进行线性搜索,直到找到所需元素为止 |
IndexOf() | IndexOf()方法与Contains()方法类似,也是对数组中的所有元素进行线性搜索。不同的是,IndexOf()方法会返回所找到的第一个元素的索引 |
Insert() Remove() RemoveAt() | 对于集合,Insert()方法用于插入元素,Remove()和RemoveAt()可删除元素。对于数组,这些方法都抛出NotSupportedException异常 |
IsFixedSize | 数组的大小总是固定的,所以这个属性总是返回true |
IsReadOnly | 数组总是可以读/写的,所以这个属性返回false。第10章将介绍如何从数组中创建只读属性 |
Item | Item属性可以用整型索引访问数组 |
5.6
枚举
在foreach语句中使用枚举,可以迭代集合中的元素,且无需知道集合中的元素个数。图5-7显示了调用foreach方法的客户机和集合之间的关系。数组或集合执行带GetEumerator()方法的IEumerable接口。GetEumerator()方法返回一个执行IEumerable接口的枚举。接着,foreach语句就可以使用IEumerable接口迭代集合了。图 5-7 |
GetEnumerator()方法用IEnumerable接口定义。foreach语句并不真的需要在集合类中执行这个接口。有一个名为GetEnumerator()的方法,返回实现了IEnumerator接口的对象就足够了。
5.6.1 IEnumerator接口
foreach语句使用IEnumerator接口的方法和属性,迭代集合中的所有元素。这个接口中的属性和方法如表5-4所示。表 5-4
IEnumerator接口的方法和属性 | 说 明 |
MoveNext() | MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法就返回false |
Current | 属性Current返回光标所在的元素 |
Reset() | Reset()方法将光标重新定位于集合的开头。许多枚举会抛出NotSupportedException异常 |
5.6.2 foreach语句
C#的foreach语句不会解析为IL代码中的foreach语句。C#编译器会把foreach语句转换为IEnumerable接口的方法和属性。下面是一个简单的foreach语句,它迭代persons数组中的所有元素,并逐个显示它们:foreach (Person p in persons) { Console.WriteLine(p); } |
IEnumerator enumerator = persons. GetEnumerator(); while (enumerator.MoveNext()) { Person p = (Person) enumerator.Current; Console.WriteLine(p); } |
5.6.3 yield语句
C# 1.0使用foreach语句可以轻松地迭代集合。在C# 1.0中,创建枚举器仍需要做大量的工作。C# 2.0添加了yield语句,以便于创建枚举器。yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代。
下面的例子是用yield
return语句实现一个简单集合的代码。类HelloCollection包含GetEnumerator()方法。该方法的实现代码包含两个yield return语句,它们分别返回字符串Hello和World。
using System; using System.Collection; namespace Wrox.ProCSharp.Arrays { public class HelloCollection { public IEumerator GetEumerator() { yield return "Hello"; yield return "World"; } } } |
包含yield语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumerator或IEnumerable接口。这个块可以包含多个yield return语句或yield break语句,但不能包含return语句。
现在可以用foreach语句迭代集合了:
public class Program { HelloCollection helloCollection = new HelloCollection(); foreach (string s in helloCollection) { Console.WriteLine(s); } } } |
类型,其中包含一个状态机,如下面的代码所示。yield 类型执行IEnumerator和IDisposable接口的属性和方法。在下面的例子中,可以把yield 类型看作内部类Enumerator。外部类的GetEnumerator()方法实例化并返回一个新的yield 类型。在yield 类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变。MoveNext()封装了迭代块的代码,设置了current变量的值,使Current属性根据位置返回一个对象。
public class HelloCollection { public IEnumerator GetEnumerator() { Enumerator enumerator = new Enumerator(); return enumerator; } public class Enumerator : IEnumerator, IDisposable { private int state; private object current; public Enumerator(int state) { this.state = state; } bool System.Collections.IEnumerator.MoveNext() { switch (state) { case 0: current = "Hello"; state = 1; return true; case 1: current = "World"; state = 2; return true; case 2: break; } return false; } void System.Collections.IEnumerator.Reset() { throw new NotSupportedException(); } object System.Collections.IEnumerator.Current { get { return current; } } void IDisposable.Dispose() { } } } |
return语句,很容易实现允许以不同方式迭代集合的类。类MusicTitles可以用
默认方式通过GetEnumerator()方法迭代标题,用Reverse()方法逆序迭代标题,用Subset()方法搜索子集:
public class MusicTitles { string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum"); public IEnumerator GetEnumerator() { for (int i = 0; i < 4; i++) { yield return names[i]; } } public IEnumerable Reverse() { for (int i = 3; i >= 0; i-) { yield return names[i]; } } public IEnumerable Subset( int index, int length) { for (int i = index; i < index + length; i++) { yield return names[i]; } } } |
MusicTitles titles = new MusicTitles(); foreach(string title in titles) { ConsoleWriteLine(title); } ConsoleWriteLine(); ConsoleWriteLine("reverse"); foreach(string title in titles.Reverse()) { ConsoleWriteLine(title); } ConsoleWriteLine(); ConsoleWriteLine("subset"); foreach(string title in titles.Subset(2, 2)) { ConsoleWriteLine(title); } |
在TicTacToe游戏中有9个域,玩家轮流在这些域中放置"十"字或一个圆。这些移动操作由GameMoves类模拟。方法Cross()和Circle()是创建迭代类型的迭代块。变量cross和circle在GameMoves类的构造函数中设置为Cross()和Circle()方法。这些域不设置为调用的方法,而是设置为用迭代块定义的迭代类型。在Cross()迭代块中,将移动操作的信息写到控制台上,并递增移动次数。如果移动次数大于9,就用yield break停止迭代;否则,就在每次迭代中返回yield类型cross的枚举对象。Circle()迭代块非常类似于Cross()迭代块,只是它在每次迭代中返回circle迭代类型。
public calss GameMoves { private IEnumerator cross; private IEnumerator circle; public GameMoves() { cross = Cross(); circle = Circle(); } private int move = 0; public IEnumerator Cross() { while (true) { Console.WriteLine("Cross, move {0}", move); move++; if (move > 9) yield break; yield return circle; } } public IEnumerator Circle() { while (true) { Console.WriteLine("Circle, move {0}", move); move++; if (move > 9) yield break; yield return cross; } } } |
GameMoves game = new GameMoves(); IEnumerator enumerator = game.Cross(); while (enumerator.MoveNext()) { enumerator = (IEnumerator) enumerator.Current; } |
Cross, move 0 Circle, move 1 Cross, move 2 Circle, move 3 Cross, move 4 Circle, move 5 Cross, move 6 Circle, move 7 Cross, move 8 |
相关文章推荐
- 数组、集合的接口和枚举介绍
- Enumeration枚举接口的简单介绍
- ATL源码学习5---集合与枚举接口支持
- 数组,集合,IEnumerable接口,迭代器
- JSE6-数组枚举,集合,泛型
- OC中集合类对象的枚举和数组的排序
- java 基础总结 -- enum 枚举与接口、泛型、集合的使用
- 字符串数组集合OC简单介绍
- Delphi基本类型--枚举 子界 集合 数组
- 集合与数组简明介绍
- 实现对象集合枚举接口
- OC 容器 数组 字典 集合 枚举
- Java学习笔记31(集合框架五:set接口、哈希表的介绍)
- C语言03_构造类型 数组, 结构体, 共用体, 枚举类型介绍
- java 集合 之 链表和线性表以及ArrayList的各方法的介绍及示例 及 ArrayList与LinkedList的区别分析 及 ArrayList与Array(数组)的区别
- 1.当形参,返回值类型是JavaBean式的复合类,List集合,数组等时:服务端接口
- 【OC加强】枚举介绍、数组的排序、对象的排序、如何利用block排序以及一些数据类型知识
- 数组,集合,IEnumerable接口,迭代器
- java 集合1--接口及父类介绍
- 枚举数组接口