前言

  IEnumerable、IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下。以备自己日后可以来翻查,同时也希望园子里的大牛们,来帮我看看理解的怎么样。

查看并使用两个接口

  接下来我们先来看看两个接口的定义。

  先来看一下IEnumerable接口,其实看过这个接口之后,发现它其实是非常的简单,只包含一个方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象,如下面截图所示:

这里的IEnumerator对象,其实就是另外一个接口,这个接口对象有什么呢?它是一个真正的集合访问器,没有它,就不能使用foreach语句遍历集合或数组,因为只有IEnumerator对象才能访问集合中的项,假如连集合中的项都访问不了,那么进行集合的循环遍历是不可能的事情了。那么让我们看看IEnumerator接口又定义了什么东西。

从上面我们知道IEnumerator接口定义了一个Current属性,MoveNext和Reset两个方法,这是多么的简约。既然IEnumerator对象是一个访问器。那至少应该有一个Current属性,来获取当前集合中的项吧。MoveNext方法只是将游标的内部位置向前移动(就是移到一下个元素而已),要想进行循环遍历,不向前移动一下怎么行呢?

通过注释也可以明确的发现他们的用处。

下面我们来看一个简单的例子:

        static void Main(string[] args)
{
int[] iArr = { 1, 3, 4, 6, 7, 9 };
foreach (int i in iArr)
{
Console.WriteLine(i.ToString());
}
Console.ReadLine();
}

F5来运行代码

结果有了,说明简单的数组是可以支持foreach循环的。

下面我们来自己来做一个小例子,先来定义实体类

    /// <summary>
/// 个人的实体类
/// </summary>
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
} /// <summary>
/// 一群人的实体类
/// </summary>
public class People
{
Person[] personList = new Person[4];
public People()
{
personList[0] = new Person() { Name = "aehyok", Age = 25 };
personList[1] = new Person() { Name = "Kris", Age = 22 };
personList[2] = new Person() { Name = "Leo", Age = 21 };
personList[3] = new Person() { Name = "Niki", Age = 23 };
}
}

如上面代码所示,一个Person类是个人的实体类,然后People类是一群人的实体类,按照和上面数组类似的格式,下面我们进行调用

        static void Main(string[] args)
{
       ///直接对一群人实例对象进行foreach
People people = new People();
foreach (Person p in people)
{
Console.WriteLine("Name:{0}\tAge{1}",p.Name,p.Age);
}
Console.ReadLine();
}

还没来得及编译,错误就来了

所以我们根据上面的讲解我们就让People类实现IEnumerable接口吧。现在先来修改People实体类。

    /// <summary>
/// 个人的实体类
/// </summary>
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
} /// <summary>
/// 一群人的实体类
/// </summary>
public class People:IEnumerable
{
Person[] personList = new Person[4];
public People()
{
personList[0] = new Person() { Name = "aehyok", Age = 25 };
personList[1] = new Person() { Name = "Kris", Age = 22 };
personList[2] = new Person() { Name = "Leo", Age = 21 };
personList[3] = new Person() { Name = "Niki", Age = 23 };
} public IEnumerator GetEnumerator()
{
return this.personList.GetEnumerator();
}
}

继承实现接口,完成该方法之后,就可以在调用时用foreach了。

注意:其实这里完全不用继承该接口。直接对GetEnumerator()方法进行实现,然后返回IEnumerator即可。

这样还可以有另外一种调用方式

        static void Main(string[] args)
{
People people = new People();
foreach (Person p in people)
{
Console.WriteLine("Name:{0}\tAge{1}",p.Name,p.Age);
}
Console.WriteLine("");
///直接获取迭代器
IEnumerator i = people.GetEnumerator();
while (i.MoveNext())
{
Person person = (Person)i.Current;
Console.WriteLine("Name:{0}\tAge{1}", person.Name, person.Age);
}
Console.ReadLine();
}

调用结果

自定义两个接口并进行实现

  上面我们是通过继承微软类库中的接口来实现的实体集合的foreach遍历。下面我们来演示一下完全通过自己创建接口来实现自己定义的实例集合的foreach遍历。首先我们来实现一个简单的迭代器。

第一步:定义一个接口IMyEnumerator,之后所有迭代器都要进行实现

    /// <summary>
/// 要求所有的迭代器全部实现该接口
/// </summary>
interface IMyEnumerator
{
bool MoveNext();
object Current{get;};
}

第二步:再定义一个接口IMyEnumerable,所有集合要实现该接口

    /// <summary>
/// 要求所有的集合实现该接口
/// 这样一来,客户端就可以针对该接口编码
/// 而无须关注具体的实现
/// </summary>
interface IMyEnumerable
{
IMyEnumerator GetEnumerator();
int Count{get;};
}

第三步:一个简单的集合类MyList,实现IMyEnumerable。

    class MyList:IMyEnumerable
{
int[] items = {0,1,2,3,4,5,6,7,8,9};
IMyEnumerator myEnumerator; public int this[int i]
{
get { return items[i]; }
set { this.items[i] = value; }
} public int Count
{
get { return items.Length; }
} public IMyEnumerator GetEnumerator()
{
if (myEnumerator == null)
{
myEnumerator = new MyEnumerator(this);
}
return myEnumerator;
} }

第四步:其实集合中也需要进行使用实现了第一步的迭代器,所以在此就是要实现这个迭代器

public class MyEnumerator:IMyEnumerator
{
int index = 0;
MyList myList;
public MyEnumerator(MyList myList)
{
this.myList = myList;
} public bool MoveNext()
{
if (index + 1 > =myList.Count)
{
index = 1;
return false;
}
else
{
index++;
return true;
}
} public object Current
{
get { return myList[index]; }
}
}

第五步:简单调用进行调试

        static void Main(string[] args)
{
///使用接口IMyEnumerable代替MyList
IMyEnumerable list = new MyList();
///得到迭代器,在循环中针对迭代器进行编码,而不是集合MyList
IMyEnumerator enumerator = list.GetEnumerator();
for (int i = 0; i < list.Count; i++)
{
object current = enumerator.Current;
Console.WriteLine(current.ToString());
enumerator.MoveNext(); }
Console.WriteLine("");
///重新创建一个新的对象
IMyEnumerable list1 = new MyList();
IMyEnumerator enumerator1 = list1.GetEnumerator();
while (enumerator1.MoveNext()) //因为此处闲下移了一位,所以从1开始
{
object current = enumerator1.Current;
Console.WriteLine(current.ToString());
}
Console.ReadLine();
}

调用结果

  其实我定义的两个接口使用的是IMyEnumerable和IMyEnumerator,这里你直接可以去掉My那么就是微软类库里面的接口了,我这里只是自定义罢了,然后我自己定义接口的方法属性,没有严格按照微软的接口进行定义,但是差不多,只需要进行简单的修正就可以进行调用。这里有一个版本的。

其实上面例子中的调用我们就可以使用foreach来调用了,那么现在我们来用foreach来调用看看。

        static void Main(string[] args)
{
MyList list=new MyList();
foreach (object obj in list)
{
Console.WriteLine(obj.ToString());
}
Console.ReadLine();
}

总结

通过上面我实现的几个简单的例子可以发现,一个类型支持foreach遍历的条件可以是:

  1、第一个方案是:这个类实现IEnumerable接口

  2、第二个方案是:这个类有一个public的GetEnumerator的实例方法(不用继承IEnumerable实现接口),并且返回类型中有public 的bool MoveNext()实例方法和public的Current实例属性。

实现了IEnmerable<T>接口的集合,是强类型的。它为子对象的迭代提供类型更加安全的方式。

自己实现了下,感觉还是懂了一些,虽然还没有彻底的搞明白,但最起码大概知道怎么回事了。有空再来看看yield关键字的用法。  

C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)的更多相关文章

  1. 学习javascript基础知识系列第三节 - ()()用法

    总目录:通过一段代码学习javascript基础知识系列 注意: 为了便于执行和演示,建议使用chrome浏览器,按F12,然后按Esc(或手动选择)打开console,在console进行执行和演示 ...

  2. C# 基础知识系列- 3 集合数组

    简单的介绍一下集合,通俗来讲就是用来保管多个数据的方案.比如说我们是一个公司的仓库管理,公司有一堆货物需要管理,有同类的,有不同类的,总而言之就是很多.很乱.我们对照集合的概念对仓库进行管理的话,那么 ...

  3. C# 基础知识系列- 10 反射和泛型(二)

    0. 前言 这篇文章延续<C# 基础知识系列- 5 反射和泛型>,继续介绍C#在反射所开发的功能和做的努力.上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后 ...

  4. C# 基础知识系列- 17 小工具优化

    0. 前言 不知道有没有动手能力强的小伙伴照着上一篇的内容写过程序呢?如果有的话,应该会在使用的时候发现以下几个问题: 每次启动都需要经过漫长的时间去遍历磁盘里的文件目录 因为数据是用的字典保存的,所 ...

  5. 基础知识系列☞C#中→属性和字段的区别

    "好吧...准备写个'基础知识系列',算是记录下吧,时时看看,更加加深记忆···" 其实本来准备叫"面试系列"... 字段.属性.你先知道的哪个概念? ***我 ...

  6. 基础知识系列☞Abstract和Virtual→及相关知识

    转载地址→http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html 在C#的学习中,容易混淆virtual方法和abstract方法的 ...

  7. 学习javascript基础知识系列第二节 - this用法

    通过一段代码学习javascript基础知识系列 第二节 - this用法 this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象.但是在javascr ...

  8. java基础解析系列(九)---String不可变性分析

    java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...

  9. C# 基础知识系列- 9 字符串的更多用法(一)

    0. 前言 在前面的文章里简单介绍了一下字符串的相关内容,并没有涉及到更多的相关内容,这一篇将尝试讲解一下在实际开发工作中会遇到的字符串的很多操作. 1. 创建一个字符串 这部分介绍一下如何创建一个字 ...

随机推荐

  1. SecureCRT 使用技巧

    VanDyke CRT 和 VanDyke SecureCRT是最常用的终端仿真程序,简单的说就是windows下登录UNIX或Liunx服务器主机的软件.二者不同的是SecureCRT支持SSH∗( ...

  2. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

  3. java常用方法总结

    最近打算换工作,还是需要补一下面试的基础知识,写了一些面试中可能会用到的常用算法.方法,以便复习 //99乘法表 /** * 1*1 * 1*1 1*2 * 1*1 1*2 1*3 * …… * */ ...

  4. jmeter的使用(四)

    jmeter如何调用java程序呢,下面做简单介绍.1.打开eclipse,新建项目,导入jmeter依赖的包ApacheJMeter_core.jar和ApacheJMeter_java.jar,这 ...

  5. uGUI练习(三) KeyBoard Navigation

    练习目标 练习通过键盘在按钮或其它Selectable类型组件上导航 步骤 创建一排的Button,及一个右边的Button 2.查看Button的属性里有一栏下拉列表Navigation,默认选择的 ...

  6. A*寻路初探 GameDev.net

    A*寻路初探 GameDev.net MulinB按:经典的智能寻路算法,一个老外写的很透彻很清晰,很容易让人理解神秘的A*算法.以下是一个中文翻译版. A*寻路初探 GameDev.net 作者: ...

  7. python将文件写成csv文件保存到本地

    举个例子: import csv import os path='/tmp/' file='test.csv' def generate_csv(path,file): if not os.path. ...

  8. Web Storage中的sessionStorage和localStorage

    html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage. sessionStorage用于本地存储一个会话(session)中的数据,这些数据只 ...

  9. Ruby Web实时消息后台服务器推送技术---GoEasy

    越来越多的项目需要用到实时消息的推送与接收,怎样用Ruby实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推 ...

  10. Kakfa分布式集群搭建

    本位以最新版本kafka_2.11-0.10.1.0版本讲述分布式kafka集群环境的搭建过程.服务器列表: 172.31.10.1 172.31.10.2 172.31.10.3 1.下载kafka ...