一、示例
using System;
using System.Collections;
namespace test1
{
class Program
{
static void Main(string[] args)
{
///<summary>什么是IEnumerable?</summary>
///IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumerator。Enumerable这个静态类型含有很多扩展方法,其扩展的目标是IEnumerable<T>。
///实现了这个接口的类可以使用Foreach关键字进行迭代(迭代的意思是对于一个集合,可以逐一取出元素并遍历之)。实现这个接口必须实现方法GetEnumerator。
///<summary>如何实现一个继承IEnumerable的类型?</summary>
///实现一个继承IEnumerable的类型等同于实现方法GetEnumerator
///该类已经实现了IEnumerable
Person[] peopleArray =
{
new Person("xu","shuai"),
new Person("zhu","xiao"),
new Person("hu","xiang"),
};
//People类实现了IEnumerable
var peopleList = new People(peopleArray);
//枚举时先访问MoveNext方法
//如果返回真,则获得当前对象,返回假,就退出此次枚举
foreach (Person p in peopleList)
{
Console.WriteLine(p.FirstName + " " + p.LastName);
}
Console.ReadKey();
}
}
/// <summary>
/// Person类
/// </summary>
public class Person
{
public Person(string fName,string lName)
{
FirstName = fName;
LastName = lName;
}
public string FirstName;
public string LastName;
}
/// <summary>
/// People类就是Person类的集合
/// 但我们不能用List<Person>或者Person[],因为他们都实现了IEnumerable
/// 我们要自己实现一个IEnumerable
/// 所以请将People类想象成List<Person>或者类似物
/// </summary>
public class People : IEnumerable
{
private readonly Person[] _people;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="pArray"></param>
public People(Person[] pArray)
{
//构造一个Person的集合
_people = new Person[pArray.Length];
for (int i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}
//实现IEnumerable需要实现GetEnumerator方法
//GetEnumerator方法需要一个IEnumerator类型的返回值
public IEnumerator GetEnumerator()
{
return new PeopleEnumerator(_people);
}
}
/// <summary>
/// 因为我们不能实例化一个接口。我们必须再写一个类PeopleEnumerator,
/// 它继承这个接口,实现这个接口所有的成员:Current属性,两个方法MoveNext和Reset。
/// Enumerator代表一个类似箭头的东西,它指向这个集合当前迭代指向的成员
/// IEnumerator接口类型对非泛型集合实现迭代
/// </summary>
public class PeopleEnumerator : IEnumerator
{
public Person[] People;
//每次运行到MoveNext或Reset时,利用get方法自动更新当前位置指向的对象
object IEnumerator.Current
{
get
{
try
{
//当前位置的对象
return People[_position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
//当前位置
private int _position = -1;
public PeopleEnumerator(Person[] people)
{
People = people;
}
//当程序运行到foreach循环中的in时,就调用这个方法获得下一个person对象
public bool MoveNext()
{
_position++;
//返回一个布尔值,如果为真,则说明枚举没有结束。
//如果为假,说明已经到集合的结尾,就结束此次枚举
return (_position < People.Length);
}
public void Reset() => _position = -1;
}
}
二、使用yield关键字实现方法GetEnumerator
2.1 第一种方式需要注意循环的次数,否则会报错
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test2
{
class Program
{
static void Main(string[] args)
{
Person[] personArray =
{
new Person("xushuai"),
new Person("huxiang"),
new Person("zhuxiao")
};
People peoples = new People(personArray);
var enumerator = peoples.GetEnumerator();
for (int i = 0; i < 5; i++)
{
enumerator.MoveNext();
if (enumerator.Current != null)
{
var currentP = (Person)enumerator.Current;
Console.WriteLine("Current is {0}", currentP.Name);
}
}
Console.ReadKey();
}
}
/// <summary>
/// Person类
/// </summary>
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
/// <summary>
/// Person的集合
/// </summary>
public class People : IEnumerable
{
private readonly Person[] _persons;
public People(Person[] persons)
{
_persons = persons;
}
public IEnumerator GetEnumerator()
{
return _persons.GetEnumerator();
}
}
}
2.2 第二种方式,不回报错,不过输出结果最后会始终输出最后的一个值,Current会一直停留在集合的最后一个元素
/// <summary>
/// Person的集合
/// </summary>
public class People : IEnumerable
{
private readonly Person[] _persons;
public People(Person[] persons)
{
_persons = persons;
}
public IEnumerator GetEnumerator()
{
foreach (var item in _persons)
{
yield return item;
}
}
}
2.3 yield 循环只循环了三次
/// <summary>
/// Person的集合
/// </summary>
public class People : IEnumerable
{
private readonly Person[] _persons;
public People(Person[] persons)
{
_persons = persons;
}
public IEnumerator GetEnumerator()
{
foreach (var item in _persons)
{
Console.WriteLine("test!");
yield return item;
}
}
}
2.4 yield只返回值,不赋值
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test3
{
class Program
{
static void Main(string[] args)
{
IEnumerable<Vector> vectors = GetVectors();
//Begin to call GetVectors
foreach (var vector in vectors)
{
vector.X = 4;
vector.Y = 4;
}
//Before this iterate, there are 3 members in vectors, all with X and Y = 4
foreach (var vector in vectors)
{
//But this iterate will change the value of X and Y BACK to 1/2/3
Console.WriteLine(vector);
}
Console.ReadKey();
}
static IEnumerable<Vector> GetVectors()
{
yield return new Vector(1, 1);
yield return new Vector(2, 3);
yield return new Vector(3, 3);
}
}
public class Vector
{
public double X { get; set; }
public double Y { get; set; }
public Vector(double x, double y)
{
this.X = x;
this.Y = y;
}
public override string ToString()
{
return string.Format("X = {0}, Y = {1}", this.X, this.Y);
}
}
}
在迭代的过程中改变集合的状态
foreach迭代时不能直接更改集合成员的值,但如果集合成员是类或者结构,则可以更改其属性或字段的值。不能在为集合删除或者增加成员,这会出现运行时异常。For循环则可以。
IEnumerable<Vector> vectors = GetVectors().ToList();