C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)
前言
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接口的糊涂认识)的更多相关文章
- 学习javascript基础知识系列第三节 - ()()用法
总目录:通过一段代码学习javascript基础知识系列 注意: 为了便于执行和演示,建议使用chrome浏览器,按F12,然后按Esc(或手动选择)打开console,在console进行执行和演示 ...
- C# 基础知识系列- 3 集合数组
简单的介绍一下集合,通俗来讲就是用来保管多个数据的方案.比如说我们是一个公司的仓库管理,公司有一堆货物需要管理,有同类的,有不同类的,总而言之就是很多.很乱.我们对照集合的概念对仓库进行管理的话,那么 ...
- C# 基础知识系列- 10 反射和泛型(二)
0. 前言 这篇文章延续<C# 基础知识系列- 5 反射和泛型>,继续介绍C#在反射所开发的功能和做的努力.上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后 ...
- C# 基础知识系列- 17 小工具优化
0. 前言 不知道有没有动手能力强的小伙伴照着上一篇的内容写过程序呢?如果有的话,应该会在使用的时候发现以下几个问题: 每次启动都需要经过漫长的时间去遍历磁盘里的文件目录 因为数据是用的字典保存的,所 ...
- 基础知识系列☞C#中→属性和字段的区别
"好吧...准备写个'基础知识系列',算是记录下吧,时时看看,更加加深记忆···" 其实本来准备叫"面试系列"... 字段.属性.你先知道的哪个概念? ***我 ...
- 基础知识系列☞Abstract和Virtual→及相关知识
转载地址→http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html 在C#的学习中,容易混淆virtual方法和abstract方法的 ...
- 学习javascript基础知识系列第二节 - this用法
通过一段代码学习javascript基础知识系列 第二节 - this用法 this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象.但是在javascr ...
- java基础解析系列(九)---String不可变性分析
java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...
- C# 基础知识系列- 9 字符串的更多用法(一)
0. 前言 在前面的文章里简单介绍了一下字符串的相关内容,并没有涉及到更多的相关内容,这一篇将尝试讲解一下在实际开发工作中会遇到的字符串的很多操作. 1. 创建一个字符串 这部分介绍一下如何创建一个字 ...
随机推荐
- luluzero的angularJs学习之路_angularJs示例代码
最近开始自学 angularJs这个前端MVC框架,感觉在前端实现MVC很酷有木有.哈哈哈... 先说说我对前端MVC的一个基本的理解吧(刚开始学习接触得还比较浅显,理解可能会有些不到位,还请各位大神 ...
- ZBrush中如何才能快速完成脸部雕刻(下)
骨骼,是一门基础艺术,几百年来一直为伟大的艺术大师所研究,它曾经,也将一直是创作现实且可信角色的关键,提高骨骼知识更将大大提高雕刻技能. 查看更多内容请直接前往:http://www.zbrush ...
- POJ 1515 Street Directions --一道连通题的双连通和强连通两种解法
题意:将一个无向图中的双向边改成单向边使图强连通,问最多能改多少条边,输出改造后的图. 分析: 1.双连通做法: 双连通图转强连通图的算法:对双连通图进行dfs,在搜索的过程中就能按照搜索的方向给所有 ...
- Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现) .
今天学习了Spinner组件,使用Spinner相当于从下拉列表中选择项目,下面演示一下Spinner的使用(分别使用ArrayAdapter和自定义Adapter实现) (一):使用ArrayAda ...
- 并发用户数与TPS之间的关系
1. 背景 在做性能测试的时候,很多人都用并发用户数来衡量系统的性能,觉得系统能支撑的并发用户数越多,系统的性能就越好:对TPS不是非常理解,也根本不知道它们之间的关系,因此非常有必要进行解释. 2 ...
- IO流的练习1 —— 随机获取文本中的信息
需求:一个文本中有几个名字,随机从中获取一个名字 分析: A:首先把文本中的数据读出 B:再把数据存储到集合中 C:产生一个随机的索引 D:打印出这个索引对应的值 public static void ...
- Android ant自动打包 crunch 报错
解决办法: 修改SDK_HOME/tool/ant/build.xml. <property name="aapt.ignore.assets" value="&l ...
- HBuilder打包ios应用
1先安装itunes在电脑上 2,在HBuilder的工具栏上的"工具"选项卡上安装ios插件
- javascript:让表单 文本框 只读,不可编辑的方法
有时候,我们希望表单中的文本框是只读的,让用户不能修改其中的信息,如使<input type="text" name="input1" value=&qu ...
- msg url