平时工作中我们经常用foreach来迭代一个集合。比如

 foreach (Student student in myClass)

Console.WriteLine(student);

基本所有的集合都能够foreach,但是必须要实现IEnumerable接口。IEnumerable接口很简单,就只有一个IEnumerator GetEnumerator() 方法。看这个方法的定义就知道,仅仅是公开了另一个接口IEnumerator。而IEnumerator才是真正的支持一个集合的迭代。IEnumerator有1个属性和2个方法。

public object Current;

public void Reset();

public bool MoveNext();

只允许读取集合的数据,而不允许修改。为了详细的讲解,我们来写一个简单的例子,就会一目了然。

首先我们创建一个学生类Student如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
public class Student
{
#region 私有变量 private readonly string _id;
private string _firstname;
private string _lastname; #endregion
#region 属性
public string ID { get { return _id; } } public string FirstName { get { return _firstname; } set { _firstname = value; } } public string LastName { get { return _lastname; } set { _lastname = value; } } #endregion #region 构造函数 public Student(string id, string firstname, string lastname)
{
this._id = id; this._firstname = firstname; this._lastname = lastname;
} #endregion
#region 重写基类object方法 public override string ToString()
{
return string.Format("{0} {1},ID:{2}", _firstname, _lastname, _id);
} public override bool Equals(object obj)
{
if (obj == null) return false;
if (Object.ReferenceEquals(this, obj)) return true;
if (this.GetType() != obj.GetType()) return false; Student objstudent = (Student)obj;
if (_id.Equals(objstudent._id)) return true; return false;
} public override int GetHashCode()
{
return _id.GetHashCode();
}
#endregion }
}

接下来我们定义一个ClassList类来承载学生。让我们先忘记Ienmerable。这个类包含一个ArrayList字段_student,在构造函数中模拟3个学生。_student是私有的,不对外公开的。

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
public class ClassList
{ #region private Members private readonly string _id; private ArrayList _students; #endregion #region Properties
public string ID { get { return _id; } }
#endregion #region Constructors public ClassList(string id)
{ this._id = id;
_students = new ArrayList() { new Student("", "John", "Smith"), new Student("", "Jane", "Doe"), new Student("", "Bob", "Johnson") }; } #endregion }
}

为了让对象支持foreach 迭代,ClassList类需要实现IEnumerable。因为我们的student是存在ArrayList对象里的,而ArrayList类已经实现了IEnumerable,我们就可以使用ArrayList类的Ienumerable。

        public IEnumerator GetEnumerator()
{ return (_students as IEnumerable).GetEnumerator(); }

最终的代码贴一下:

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
public class ClassList:IEnumerable
{ #region private Members private readonly string _id; private ArrayList _students; #endregion #region Properties
public string ID { get { return _id; } }
#endregion #region Constructors public ClassList(string id)
{ this._id = id;
_students = new ArrayList() { new Student("", "John", "Smith"), new Student("", "Jane", "Doe"), new Student("", "Bob", "Johnson") }; } #endregion public IEnumerator GetEnumerator()
{ return (_students as IEnumerable).GetEnumerator(); }
}
}

然后我们调用看看使用ArrayList的Ienumerable效果:

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
class Program
{
static void Main(string[] args)
{
ClassList myClass = new ClassList("History 204"); foreach (Student student in myClass) Console.WriteLine(student); Console.ReadLine();
} } }

看来还是实现了效果。那么接下来我们看看自定义实现IEnumerable。实现IEnumerable其实只要实现IEnumerator接口就可以了。

我们创建我们自己的一个自定义类ClassEnumerator 来实现IEnumerator来完成和上面相同的结果。这个类基本上就只是通过_students的索引来进行迭代,Reset()方法就是把索引设置为-1.Current属性来获取当前的student,MoveNext()来跳到Current的下一个数据,并返回一个boolean来表示是否到了集合最后。

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
public class ClassEnumerator : IEnumerator
{ private ClassList _classList; private int _index; public ClassEnumerator(ClassList classList)
{
this._classList = classList; _index = -;
} #region IEnumerator Members public void Reset()
{
this._index = -;
} public object Current
{
get { return _classList.Students[_index]; }
} public bool MoveNext()
{
_index++;
if (_index >= _classList.Students.Count)
return false;
else
return true; }
#endregion }
}

最后修改我们的ClassLst类:

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
public class ClassList : IEnumerable
{ #region private Members private readonly string _id; private ArrayList _students; #endregion #region Properties
public string ID { get { return _id; } } public ArrayList Students { get { return _students; } }
#endregion #region Constructors public ClassList(string id)
{ this._id = id;
_students = new ArrayList() { new Student("", "John", "Smith"), new Student("", "Jane", "Doe"), new Student("", "Bob", "Johnson") }; } #endregion public IEnumerator GetEnumerator()
{ return (IEnumerator)new ClassEnumerator(this); }
}
}

可已看到还是相当简单的。运行结果和上面是一样的。

下来看看 foreach怎么工作。

其实foreach只是语法糖,最终会被CLR翻译成

             IEnumerator enumerator = myClass.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine((Student)enumerator.Current); }

我们可以把foreach 换成这样试一下,结果是一样滴。

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace IenumerableDemo
{
class Program
{
static void Main(string[] args)
{
ClassList myClass = new ClassList("History 204"); //foreach (Student student in myClass) // Console.WriteLine(student); IEnumerator enumerator = myClass.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine((Student)enumerator.Current); } Console.ReadLine();
} } }

在自己的对象里实现IEnumerator和IEnumerable的更多相关文章

  1. JavaScript怎么把对象里的数据整合进另外一个数组里

    https://blog.csdn.net/qq_26222859/article/details/70331833 var json1 = [ {"guoshui":[ 3000 ...

  2. vue 监听对象里的特定数据

    vue  监听对象里的特定数据变化 通常是这样写的,只能监听某一个特定数据 watch: { params: function(val) { console.log(val) this.$ajax.g ...

  3. vue :class 可以接收 字符串 数组 和 对象 对象里面的key值 根据true或false 显示不显示

    vue :class 可以接收 字符串 数组 和 对象 对象里面的key值 根据true或false 显示不显示 https://cn.vuejs.org/v2/guide/class-and-sty ...

  4. promise对象里resolve和reject状态讲解及Promise.all()的使用

    首先来说下同步异步与阻塞非阻塞的概念,同步异步与阻塞非阻塞并没有关系.同步异步主要是事情做完以后,如何进行处理.或者说关注的是一种消息通信机制. 同步的情况下,是由处理消息者自己去等待消息是否被触发: ...

  5. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  6. 2021年了,`IEnumerator`、`IEnumerable`还傻傻分不清楚?

    IEnumerator.IEnumerable这两个接口单词相近.含义相关,傻傻分不清楚. 入行多年,一直没有系统性梳理这对李逵李鬼. 最近本人在怼着why神的<其实吧,LRU也就那么回事> ...

  7. C#深度学习の枚举类型(IEnumerator,IEnumerable)

    一.关于枚举的含义 .Net提供了可枚举类型的接口IEnumerable和枚举器接口IEnumerator,程序集System.Collections 另: IQueryable 继承自IEnumer ...

  8. IEnumerator和IEnumerable详解

    IEnumerator和IEnumerable 从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思. 了解了两个接口代表的含义后,接着看源码: IEnumerat ...

  9. 【安卓开发】为什么不能往Android的Application对象里存储数据

    在一个App里面总有一些数据需要在多个地方用到.这些数据可能是一个 session token,一次费时计算的结果等.通常为了避免activity之间传递对象的开销 ,这些数据一般都会保存到持久化存储 ...

随机推荐

  1. [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)

    上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...

  2. SharePoint 基于 REST API使用简介

    之前已经介绍了SP2010中支持CSOM的API进行远程访问SharePoint,但是CSOM的API仍然有一定的局限性,首先使用CSOM类库是基于.Net的,因此也将使用CSOM限制在了.Net平台 ...

  3. [BTS] Faulting application name: BTSNTSvc.exe, version: 3.9.469.0, time stamp: 0x4c547e09

    Log Name: ApplicationSource: Application ErrorDate: 8/22/2013 1:28:35 AMEvent ID: 1000Task Category: ...

  4. python获取当前日期前后N天或N月的日期

    # -*- coding: utf-8 -*- '''获取当前日期前后N天或N月的日期''' from time import strftime, localtime from datetime im ...

  5. javascript图片懒加载与预加载的分析

    javascript图片懒加载与预加载的分析 懒加载与预加载的基本概念.  懒加载也叫延迟加载:前一篇文章有介绍:JS图片延迟加载 延迟加载图片或符合某些条件时才加载某些图片. 预加载:提前加载图片, ...

  6. [2]. jekyll安装与应用

    一.ruby安装 这里在win下安装ruby,对应自己电脑的操作系统位数,如我的是64位,对应下载Ruby 2.0.0-p353 (x64)这个版本的ruby.然后安装过程就很简单了: 命令行下输入r ...

  7. paip.关于动画特效原理 html js 框架总结

    paip.关于动画特效原理 html js 框架总结 1. 动画框架的来源:flex,jqueryui 3 2. 特效的分类 3 2.1. Property effects 动态改变一个或多个目标对象 ...

  8. c++builder调用VC的dll以及VC调用c++builder的dll

    解析__cdecl,__fastcall, __stdcall 的不同:在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式. 比如说__cdecl,它是标准的c方法的堆栈调用方式,就 ...

  9. AndroidStudio使用第三方jar包报错(Error: duplicate files during packaging of APK)

    http://www.kwstu.com/ArticleView/android_201410252131196692 错误描述: Error: duplicate files during pack ...

  10. eclipse web项目转maven项目

    ps:好久没写博客了,工作了人就懒了,加油加油,up,up 1 eclipse web项目目录 /web app src com.xx.xx *.properties *.xml WebRoot ​W ...