C# 学习之路(十八)

Posted Moly的太阳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 学习之路(十八)相关的知识,希望对你有一定的参考价值。

枚举集合


本系列文章主要意在总结笔者在学习过程中学到的有关 C# 特性的知识,分享 C# 中较为重要和突出的部分和有助养成良好编程习惯的提示。

并非旨在系统介绍 C#。




    今天主要分享枚举器、迭代器和枚举集合的相关知识,这是我很早就想放在文章中分享的部分。

绿色高亮:IEnumerable

棕色高亮:IEnumerator 

蓝色高亮:其他代码

C# 学习之路(十八)




目录: 

  • 枚举集合和枚举器

  • 迭代器实现枚举器




枚举集合和枚举器:

    小读者们对 foreach 语句肯定不陌生吧!我们可以用 foreach 语句对数组进行正向遍历。我们还可以用 foreach 语句去遍历一个 List<T> 类:

List<int> numbers = new List<int>();
// 初始化集合foreach (int number in new int[12]{ 10, 9, 8, 7, 7, 6 ,5 ,10, 4, 3, 2, 1 }){ numbers.Add(number); }
// 用 foreach 语句遍历 List<T> 集合foreach (int number in numbers){ Console.WriteLine(number);}

    foreach 语句无疑极大简化了需要编写的代码,但它的使用也是有条件的!这个条件就是:foreach 只可用于遍历可枚举集合。

    什么是可枚举集合?简单地说,就是实现了 System.Collections.IEnumerable 接口的集合。(下面简写为 IEnumerable )

    需要特别提醒的是,C# 所有数组都是 System.Array 类的实例,而 System.Array 类是实现了 IEnumerable 接口的类。因此所有数组都支持 foreach 语句。

    IEnumerable 接口只包含一个名为 GetEnumerator 方法。因此我们只需要实现这个方法,就能完成为接口 IEnumerable 的实现。GetEnumerator 方法的原型如下:

IEnumerator GetEnumerator();

    可以发现,GetEnumerator 方法的返回值为 IEnumerator 。好家伙,这是要我返回一个接口,我咋不记得有返回一个接口的方法?读者可能会有这样的疑惑。但其实不然,返回值是 IEnumerator 表示的是我们需要返回一个实现了 IEnumerator 接口的对象!这就好比返回值类型是 object 不一定真的就要返回 object 变量,将返回值类型设置成 object 只是为了囊括所有继承自 object 的类。将返回值类型设置为 IEnumerator 也是同样的道理。 

    一个实现了 IEnumerator 接口的对象被称为枚举器对象(以下简称枚举器)。因此方法 GetEnumerator 正如其名,返回一个枚举器(Enumerator)。

    那么现在,我们显然就要讨论实现 IEnumerator 接口的类又需要实现哪些部分了。(请注意,IEnumerable 和 IEnumerator 是不一样的!虽然它们的拼写很类似)

    IEnumerator 接口制定了以下属性和方法:

object Current { get; }bool MoveNext();void Reset();

    枚举器要实现以上代码段中声明的方法和属性。可以将枚举器想象成指向实现了 IEnumerable 接口的对象中的元素的指针。放在 List<T> 类中,就可以说 List<T> 的枚举器是指向 List<T> 类中的元素的指针。

    指针最开始指向第一个元素之前的位置,一般第一个元素的位置是 0 ,那么第一个元素之前的位置可以理解为位置 -1。调用 MoveNext 方法,就可使指针移至列表中的下一项;如果能实际地移到下一项,MoveNext 方法返回 true,否则返回 false。可用 Current 属性访问当前指向的那一项(这就是为啥它是 object 类型的);使用 Reset 方法,则可使指针回到列表第一项之前的位置,也就是位置 -1。

    实现了 IEnumerable 接口的类通过集合的 GetEnumerator 方法创建枚举器,然后反复调用枚举器的 MoveNext 方法,并获取 Current 属性的值,就可以每次在该集合中移动一个元素的位置。这正是 foreach 语句做的事情。所以,为了创建自己的可枚举集合类,就必须在自己的集合中实现 IEnumerable 接口,并提供 IEnumerator 接口的一个实现(枚举器),以便由集合类的 GetEnumerator 方法返回。

    稍微想一下,就会发现 IEnumerator 接口的 Current 属性具有非类型安全的行为,因为它返回 object 而非具体类型。幸好,.NET Framework 类库还提供了泛型 IEnumerator<T> 接口,该接口同样有 Current 属性,但返回的是一个 T。类似地,还有一个 IEnumerable<T> 接口,其中的 GetEnumerator 方法返回的是一个实现了 IEnumerator<T> 接口的枚举器对象。

    应尽可能使用泛型版本的 IEnumerable 和 IEnumerator 接口。


    我们来看一个来自微软文档的实例:

using System;using System.Collections;
// 一个待会要被使用的类public class Person{ public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; }
public string firstName; public string lastName;}
// 实现 IEnumerable 接口的类,也就是支持 foreach 语句的类public class People : IEnumerable{ private Person[] _people; // 利用先前定义的 Person 类 public People(Person[] pArray) { _people = new Person[pArray.Length];
for (int i = 0; i < pArray.Length; i++) { _people[i] = pArray[i]; } }
IEnumerator IEnumerable.GetEnumerator()    {       return (IEnumerator) GetEnumerator();        // 此 GetEnumerator 非 IEnumerable.GetEnumerator 方法,其定义就在下方 }
    // 这个 GetEnumerator 方法用于返回一个 PeopleEnum 枚举器 public PeopleEnum GetEnumerator() { return new PeopleEnum(_people); }}
// 枚举器本尊public class PeopleEnum : IEnumerator{ public Person[] _people;    // 这个 position 变量代表元素位置 int position = -1;
// 初始化枚举器 public PeopleEnum(Person[] list) { _people = list; }
public bool MoveNext() { position++; return (position < _people.Length); }
public void Reset() { position = -1; }
public Person Current { get { try { return _people[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } }
object IEnumerator.Current { get { return Current; } }}
class App{ static void Main() { Person[] peopleArray = new Person[3] { new Person("John", "Smith"), new Person("Jim", "Johnson"), new Person("Sue", "Rabon"), };
People peopleList = new People(peopleArray); foreach (Person p in peopleList) Console.WriteLine(p.firstName + " " + p.lastName); }}
/* This code produces output similar to the following: * * John Smith * Jim Johnson * Sue Rabon * */

    图片版:

C# 学习之路(十八)

    为了大

以上是关于C# 学习之路(十八)的主要内容,如果未能解决你的问题,请参考以下文章

Spark学习之路 (十八)SparkSQL简单使用

Spark学习之路 (十八)SparkSQL简单使用[转]

我的学习之路_第十八章_SQL语句

Hadoop学习之路(十八)MapReduce框架Combiner分区

Spark学习之路 (二十八)分布式图计算系统

我的学习之路_第二十八章_JQuery 和validator插件