首先我们来说下迭代的目的:
迭代是将自定义类变为自定义类数组,并遍历它
在C#1.0和C#2.0这两个版本中,C#2比C#1多了一个更简洁的迭代方法
1、定义单个类—>2、定义这个类的数组的枚举器方法—>3、定义GetEnumerator方法—>4、对该类数组赋值,并实例化3GetEnumerator方法,并将赋值的数组作为实参传入这个方法,进而传到枚举器—>5、调用foreach,对该类数组进行遍历
using System;
using System.Collections;
public class Person
{
public string firstName, lastName;
public Person (string fName,string lName)
{
firstName = fName;
lastName = lName;
}
}
public class PeopleEnum : IEnumerator
{
public Person[] _people;
int Position = -1;
public PeopleEnum(Person[] List)
{
_people = List;
}
public bool MoveNext()
{
Position++;
return (Position < _people.Length);
}
public void Reset()
{
Position = -1;
}
public object Current
{
get
{
try
{
return _people[Position];
}
catch (IndexOutOfRangeException)
{
throw new IndexOutOfRangeException();
}
}
}
}
public class People : IEnumerable
{
private Person[] _people;
public People(Person[] pArray)
{
_people = pArray;
}
public IEnumerator GetEnumerator()
{
return new PeopleEnum(_people);
}
}
class Program
{
static void Main()
{
Person[] peopleArray = new Person[3]
{
new Person("A","a"),
new Person("B","b"),
new Person("C","c")
};
People peopleList = new People(peopleArray);
foreach(Person p in peopleList)
{
Console.WriteLine("firstName:" + p.firstName + " lastName:" + p.lastName);
}
}
}
定义:迭代器是C#2.0中的新功能,迭代器块可以是方法主体、访问器主体或运算符主体,他能够使您在类或结构中支持foreach迭代,而不必实现整个IEnumerable接口(不实现它其中的枚举器接口),只需提供一个迭代器,即可遍历类中的数据结构,当编译器检测到迭代器时,他将自动生成IEnumerable接口的Current、MoveNext、Dispose方法。迭代器需引用System.Collections.Generic命名空间
移除C#1.0的枚举器方法PeopleEnum,在继承了IEnumerable接口的People类中,将GetEnumerator方法中代码改为如下代码,用迭代器创建枚举器
public IEnumerator GetEnumerator()
{
for(int i = 0; i < _people.Length; i++)
{
yield return _people[i];
}
}
即GetEnumerator方法不再需要枚举器了,而是内部的迭代器yield return语句告诉编译器来自动为我们创建枚举器,yield return语句指定了枚举器中下一个可枚举项
C#1我们要写单个类、IEnumerable接口的实现类(内有GetEnumerator方法)、枚举器类,才能在Main方法实例该类数组遍历它,
C#2我们仅写单个类、IEnumerable接口的实现类,不必写枚举器类,便可在Main方法实例该类数组并遍历它了
案例
using System;
using System.Collections.Generic;
class MyClass
{
public IEnumerator<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib();
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc)
Console.WriteLine(c);
}
}
类a包含GetEnumerator方法和一个可枚举类型的迭代器,该可枚举类型迭代器内部又自动实现了GetEnumerator方法和一个可枚举类型
要想遍历该类a,类a的GetEnumerator方法需获取可枚举类型的GetEnumerator方法,从而获得可枚举类型迭代器内部的枚举器
a、迭代器创建class类型
using System;
using System.Collections.Generic;
class MyClass
{
public class AS
{
public int caa;
}
public IEnumerable<AS> ib()
{
yield return new AS { caa = 10 };
yield return new AS { caa = 20 };
yield return new AS { caa = 30 };
}
public IEnumerator<AS> GetEnumerator()
{
return ib().GetEnumerator();
}
static void Main()
{
MyClass mc = new MyClass();
foreach (AS c in mc)
Console.WriteLine(c.caa);
}
}
b、迭代器创建string类型
using System;
using System.Collections.Generic;
class MyClass
{
public IEnumerable<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib().GetEnumerator();
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc)
Console.WriteLine(c);
}
}
迭代器创建枚举器:仅生成了一个枚举器,GetEnumerator方法直接获取迭代器(迭代器返回枚举器)
public IEnumerator<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib();
}
迭代器创建可枚举类型:生成了一个枚举器和一个GetEnumerator方法。迭代器外的GetEnumerator方法获取迭代器内的GetEnumerator()方法,从而间接获得迭代器的枚举器,可对该总类进行遍历
public IEnumerable<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib().GetEnumerator();
}
当我们实现返回枚举器的迭代器时,必须要实现GetEnumerator来让类可枚举,GetEnumerator方法返回由迭代器返回的枚举器
using System;
using System.Collections.Generic;
class MyClass
{
public IEnumerator<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib();
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc)
Console.WriteLine(c);
}
}
我们可让总类实现GetEnumerator来让总类本身可被枚举,也可不实现来让类本身不可枚举。但即使不实现GetEnumerator方法,也能实现枚举
a、实现总类的GetEnumerator方法:迭代器方法ib内自动创建了一个枚举器和一个GetEnumerator方法,如果实现总类的GetEnumerator方法,它返回迭代器方法ib返回的GetEnumerator方法(这个方法获取到迭代器的枚举器),从而间接获取到的ib内自动创建的枚举器
using System;
using System.Collections.Generic;
class MyClass
{
public IEnumerable<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
public IEnumerator<string> GetEnumerator()
{
return ib().GetEnumerator();
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc)
Console.WriteLine(c);
}
}
b、不实现GetEnumerator也可枚举的方法:如果不实现总类的GetEnumerator方法,Main方法直接采用IEnumerable类型的迭代器(内有GetEnumerator方法),那么就不需要总类的GetEnumerator方法了
using System;
using System.Collections.Generic;
class MyClass
{
public IEnumerable<string> ib()
{
yield return "caa0";
yield return "caa1";
yield return "caa2";
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc.ib())
Console.WriteLine(c);
}
}
在一个类中产生多个可枚举类型,那么就不能能实现GetEnumerator方法,因为可枚举类型的GetEnumerator方法主体有固定格式,无法再次写个GetEnumerator方法,那样就会重名,且Main方法调用的也不知是哪个正确的GetEnumerator方法
一个类产生多个可枚举类型,而是采用不实现GetEnumerator的方法,Main方法直接调用返回可枚举类型的迭代器,即上文的“b、不实现GetEnumerator也可枚举的方法”
using System;
using System.Collections.Generic;
class MyClass
{
string[] colos = { "violet", "blue", "cyan", "green" };
public IEnumerable<string> Method1()
{
for (int i = 0; i < colos.Length; i++)
yield return colos[i];
}
public IEnumerable<string> Method2()
{
for (int i = colos.Length-1; i >= 0; i--)
yield return colos[i];
}
static void Main()
{
MyClass mc = new MyClass();
foreach (string c in mc.Method1())
Console.WriteLine(c);
foreach (string c in mc.Method2())
Console.WriteLine(c);
}
}
public IEnumerator<string> shuxing
{
get
{
for (int i = 0; i < colos.Length; i--)
yield return colos[i];
}
}
上文讲述的都是将迭代器作为方法。再次重申一下,迭代器块可作为方法主体、访问器块主体和运算符主体
在编译器生成的IEnumerator枚举器方法中,Reset方法(用来重置)并没有实现,因此调用它会抛出System.NotSpportedception异常。
大家还有什么问题,欢迎在下方留言!