IEnumerator和IEnumerable的作用

其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持foreach循环,至于foreach循环,如果不清楚,请参考C# foreach循环较for循环的优势与劣势

代码如下:

static void Main(string[] args)
{
CatList cats = new CatList();
foreach (var cat in cats)
{
Console.WriteLine(cat.NickName);
}
} public class CatList
{
public string NickName { get; set; }
public int Age { get; set; }
}

以上代码说明自定义集合类型(假设CatList是集合类型)是无法使用foreach进行循环的.

原因是C#中自定义集合类型要实现foreach的功能,必须通过IEnumerator和IEnumerable两个接口来实现!

1、通过IEnumerator和IEnumerable两个接口实现自定义集合类型的foreach循环功能.    非泛型版本

第一步:实现自定义集合类型实现IEnumerable接口,实现该接口的字面意思可以理解为:自定义集合类型实现了该接口,就拥有了"可枚举的功能".代码如下:

实现了IEnumerable接口之后,发现该接口规定必须返回一个IEnumerator接口(迭代器对象).ok,那么就必须返回一个IEnumerator,那么它是什么呢?别急,下面开始介绍这个接口!

第二步:通过IEnumerable要求实现的GetEnumerator()方法返回一个IEnumerator(迭代器对象),实现该接口必须实现以下三个方法/属性:

(1)、MoveNext()   ---将当前遍历的枚举数推进到集合的下一个.

注:如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 false。当枚举数位于此位置时,对MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext

(2)、Current属性   ---返回正在遍历的集合中的元素,注:该属性没有set方法,所以在循环遍历的时候不能设置值.

(3)、Reset()   ---重置当前正在遍历的集合中元素的索引.

第三步:具体实现

在介绍完上面两个接口之后,开始具体的实现,现在需要编写一个People类,该类是一个Person集合,要求People类拥有foreach循环的功能,代码如下:

public class People : IEnumerable
{
private Person[] persons; public People(Person[] persons)
{
Persons = persons;
} public Person[] Persons { get => persons; set => persons = value; } public IEnumerator GetEnumerator()
{
return new PersonEnum(Persons);
} public class PersonEnum : IEnumerator
{
private Person[] perons;
private int _index=-; public PersonEnum(Person[] persons)
{
Perons = persons;
} public object Current => Perons[_index]; public Person[] Perons { get => perons; set => perons = value; } public bool MoveNext()
{
_index++;
if (_index < perons.Length)
return true;
else
return false;
} public void Reset()
{
_index = ;
}
}
}

第四步:验证代码,代码如下:

Person[] persons ={
new Person(){FirstName="Stephen",LastName="Curry"},
new Person(){FirstName="Lebron",LastName="James"},
new Person(){FirstName="Kobe",LastName="Brant"}
};
People people = new People(persons);
foreach (var p in people)
{
Console.WriteLine(((Person)p).LastName);
}

第五步:分析原理

总结分析下上面的代码,实现foreach代码的基本原理如下:

1、编写自定义集合类,实现IEnumerable接口,通过GetEnumerator()方法返回一个迭代器对象实例.

2、通过自定义集合类的构造函数,或者方法,或者属性(都可以),初始化一个类数组   !Important

3、将初始化完的类数组作为参数传递给迭代器类

4、编写迭代器类,create 构造函数,接收自定义集合类初始化完的类数组

5、实现IEnumerator(迭代器)接口,实现对应的三个方法,通过编写三个方法发现,其实迭代器就是简单的对数组进行的操作

第六步:执行自定义集合的循环

执行方式有两种:

(1)、foreach

Person[] persons ={
new Person(){FirstName="Stephen",LastName="Curry"},
new Person(){FirstName="Lebron",LastName="James"},
new Person(){FirstName="Kobe",LastName="Brant"}
}; People people = new People(persons);
foreach (Person p in persons)
{
Console.WriteLine(p.LastName);
}
Console.ReadKey();

(2)、通过自定义集合类的GetEnumerator()方法

Person[] persons ={
new Person(){FirstName="Stephen",LastName="Curry"},
new Person(){FirstName="Lebron",LastName="James"},
new Person(){FirstName="Kobe",LastName="Brant"}
}; People people = new People(persons);
IEnumerator er = people.GetEnumerator();
while (er.MoveNext())
{
Console.WriteLine(((Person)er.Current).LastName);
}

分析两种不同的调用方式,foreach语句可以理解为是第二种方式的语法糖.

2、通过IEnumerable<T>和IEnumerator<T>实现自定义集合类,并实现简单的添加功能

    class Program
{
static void Main(string[] args)
{
var list = new CustomList<int>();
list.Add();
list.Add();
list.Add(); var p = new int[] { , , };
var list1 = new CustomList<int>(p);
Console.WriteLine(list.Count+"..."+list1.Count);
Console.ReadKey();
}
} public class CustomList<T> : IEnumerable<T>
{
private T[] _ts; private int _index = ; public CustomList(T[] ts)
{
_ts = ts;
_index = ts.Length;
} public CustomList(int capcity)
{
_ts = new T[capcity];
} public int Count => _index; public void Add(T t)
{
if (_index >= _ts.Length)
{
//如果调用Add方法导致数组的长度大于我们给定的长度
//则创建一个新的数组,并将其长度扩大为原来的两倍
T[] newArr = new T[_ts.Length * ]; //将原来数组中的数据拷贝到新的数组中
Array.Copy(_ts, newArr, _ts.Length); //使_ts指向新的数组
_ts = newArr;
}
_ts[_index++] = t;
} public IEnumerator<T> GetEnumerator()
{
return new CustomEnumerator<T>(_ts);
} IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
} public class CustomEnumerator<T> : IEnumerator<T>
{
private T[] _ts; private int _position = -; public CustomEnumerator(T[] ts)
{
_ts = ts;
} public T Current => _ts[_position]; object IEnumerator.Current => this.Current; public bool MoveNext()
{
_position++;
if (_position < _ts.Length)
return true;
return false;
} public void Reset()
{
_position = ;
} public void Dispose()
{
throw new NotImplementedException();
}
}

C# 通过IEnumberable接口和IEnumerator接口实现泛型和非泛型自定义集合类型foreach功能的更多相关文章

  1. C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能

    1.IEnumerator和IEnumerable的作用 其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持foreach循环,至于foreach循环 ...

  2. 细说 C# 中的 IEnumerable和IEnumerator接口

    我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...

  3. 迭代器学习之一:使用IEnumerable和IEnumerator接口

    写博客是检验我学习的成果之一以及自我总结的一种方式,以后会经常利用这种方式进行技术交流和自我总结,其中认识不深难免会有错误,但是一直懂得不懂就问,不懂就学的道理! 1.首先看一个简单的列子 , , , ...

  4. foreach为什么要实现IEnumerable接口而不是直接用IEnumerator接口

    在.Net中,要想被foreach遍历,那么目标对象要实现IEnumerable或IEnumerable<T>接口,这个接口有一个方法,GetEnumerator(),返回一个IEnume ...

  5. C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)

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

  6. C# IEnumerable 和 IEnumerator接口浅析

    温故而知新,可以为师矣,有空经常复习一下基础知识是有必要的,并且能加深理解和记忆. Foreach常用于循环访问集合,对实现IEnumerable的接口的容器进行遍历,IEnumerable和IEnu ...

  7. IEnumerable和IEnumerator接口

    我们先思考几个问题:1.为什么在foreach中不能修改item的值?(IEnumerator的Current为只读)2.要实现foreach需要满足什么条件?(实现IEnumerator接口来实现的 ...

  8. IEnumerable, IEnumerator接口

    IEnumerable接口 // Exposes the enumerator, which supports a simple iteration over a non-generic collec ...

  9. IEnumerable、IEnumerator接口(如何增加迭代器功能)

    IEnumerable.IEnumerator接口封装了迭代器功能,有了它,我们不需要将内部集合暴露出去,外界只需要访问我的迭代器接口方法即可遍历数据. 在C#中,使用foreach语句来遍历集合.f ...

随机推荐

  1. phonegap android插件,启动activity并返回值

    Your execute menthod is not quite right. When you do: return new PluginResult(PluginResult.Status.OK ...

  2. IntelliJ IDEA 2017版 编译器使用学习笔记(二) (图文详尽版);IDE快捷键使用

    补充介绍IntellJ 介绍主菜单功能及相关用途: File -------------> 对文件进行操作 Edit ------------> 对文本进行操作 View -------- ...

  3. Java(Android)线程池[转]

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  4. 20155202 2016-2017-2 《Java程序设计》第6周学习总结

    20155202 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 输入输出 数据从来源取出:输入串流 java.io.InputStream 写入目的的:输出 ...

  5. hdu 2845 Beans 2016-09-12 17:17 23人阅读 评论(0) 收藏

    Beans Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  6. Paul and Joyce are going to a movie(More listening of Unit 2)

      Paul: Hurry up, Joyce. We need to leave now if we're going to get to the theater a half hour befor ...

  7. Mina的ssl加密

    前面写的Mina的示例,都是可以通过telnet登录的,并且可以相互交互. 现在采用ssl加密的方式,对建立连接时,进行加密连接.这样,双方只有统一的加密,然后才可以连接. 密钥的生成之前有说过,里面 ...

  8. [翻译]NUnit---SetUp and SetUpFixture and Suite Attributes(十九)

    SetUpAttribute (NUnit 2.0 / 2.5) 本特性用于TestFixture提供一个公共的功能集合,在呼叫每个测试方法之前执行.同时也用在SetUpFixture中,SetUpF ...

  9. [ASP.NET]Net Framework环境问题的一种修复方案

    一.情况介绍 造价软件基于.net framework 4.0开发,要成功运行需要在目标电脑上安装4.0版本以上的framework.一般情况下xp是没有的,win7系列自带3.5,都需要手动安装4. ...

  10. 毕业回馈-89C51之数码管的使用

    7段码的数码管由7个LED等共同组成,根据公共端的不同有共阴和共阳之分.现在很多数码管在7段码的基础上加了一个.即dp,其内部结构如下图所示: 公共端为LED灯的阴极,所以为共阴极接法: 公共端为阳极 ...