IEnumerable、IEnumerator与yield的学习

时间:2023-11-22 18:09:56

我们知道数组对象可以使用foreach迭代进行遍历,同时我们发现类ArrayList和List也可以使用foreach进行迭代。如果我们自己编写的类也需要使用foreach进行迭代时该怎么办呢?

IEnumerable:

 public interface IEnumerable
{
IEnumerator GetEnumerator();
}

如果自己编写的类需要foreach进行迭代就需要实现IEnumerable接口,表示当前的类可以进行迭代。

我们发现该接口唯一的方法返回的是另一个接口IEnumerator,下面看看这个接口是干嘛的。

IEnumerator:

 public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}

如果说IEnumerable接口是表示当前类可以进行迭代,那么IEnumerator则是实现迭代逻辑的接口,我们需要编写一个实现IEnumerator接口的类并在其中编写好迭代逻辑。

下面直接上一个例子:

People.cs:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Test
{
/// <summary>
/// 自定义的可迭代类.
/// </summary>
class People : IEnumerable<Person>
{
//这里用了一个 List 有点无聊, 因为 List 本身就可以进行迭代, 为了写例子没办法
private List<Person> _list; public People()
{
_list = new List<Person>();
} public IEnumerator<Person> GetEnumerator()
{
return new PeopleEnumerator(_list.ToArray());
} //示例程序所以这里就添加一个方法就行了
public void AddPerson(Person person)
{
_list.Add(person);
} System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
} /// <summary>
/// 迭代器的逻辑实现类.
/// </summary>
class PeopleEnumerator : IEnumerator<Person>
{
public Person[] pers; private int index = -; public PeopleEnumerator(Person[] pers)
{
this.pers = pers;
} public Person Current
{
get
{
return pers[index];
}
} public bool MoveNext()
{
index++;
return index < pers.Length;
} public void Reset()
{
index = -;
} public void Dispose()
{
pers = null;
} object System.Collections.IEnumerator.Current
{
get { return Current; }
}
} /// <summary>
/// 集合的元素.
/// </summary>
class Person
{
public string name;
public bool isMale; public Person(string name, bool isMale)
{
this.name = name;
this.isMale = isMale;
}
}
}

Program.cs:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Test
{
class Program
{
static void Main(string[] args)
{
new Program(); Console.ReadKey();
} public Program()
{
People people = new People();
people.AddPerson(new Person("tony", true));
people.AddPerson(new Person("tony mom", false));
people.AddPerson(new Person("alen", true));
people.AddPerson(new Person("gilbret", true));
people.AddPerson(new Person("mark", false)); foreach(Person person in people)
{
Console.WriteLine("Name: {0}, sex is male:{1}", person.name, person.isMale);
}
}
}
}

下面是运行结果:

 Name: tony, sex is male:True
Name: tony mom, sex is male:False
Name: alen, sex is male:True
Name: gilbret, sex is male:True
Name: mark, sex is male:False

yield:

yield 是 C# 提供的一个特殊的用于迭代的语法,其可以简化迭代实现的代码,yield return 语句返回集合的一个元素,并移动到下一个元素上,yield break 可以停止迭代。

头晕了吧?没关系,我们先看看一个简单的例子:

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Test
{
class Program
{
static void Main(string[] args)
{
new Program(); Console.ReadKey();
} public Program()
{
People people = new People(); foreach(string name in people)
{
Console.WriteLine(name);
}
}
} class People : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return "gilbert";
yield return "alen";
yield return "grace";
}
}
}

运行的结果为:

 gilbert
alen
grace

没错,当程序碰到yield return这个语句时就将其后面附带的数据作为current返回,同时程序会再此处暂停,运行结束foreach中的代码后再继续,同时执行的是下一个语句了,我们再看看yield break的效果,该效果表示立即停止迭代,示例如下:

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Test
{
class Program
{
static void Main(string[] args)
{
new Program(); Console.ReadKey();
} public Program()
{
People people = new People(); foreach(string name in people)
{
Console.WriteLine(name);
}
}
} class People : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return "gilbert";
yield return "alen";
yield break;//指示这里要停止迭代
yield return "grace";
}
}
}

运行的结果为:

 gilbert
alen

最后要说一下:包含yoeld语句的方法或者属性也称为迭代块,迭代块必须声明为返回IEnumerator或IEnumerable接口,迭代块可以包含多个yield return或yield break语句,但是不能包含return语句。

不要小看yield迭代快,下一篇笔记我要可转回U3D了,我们要详细的看看yield在U3D里的变种——协程