为什么可以foreach呢?我们自定义的类能不能foreach?下面请看测试
Person p = new Person();
foreach (string item in p)
{
Console.WriteLine(item);
}
Console.WriteLine("ok");
Console.ReadKey();
编译错误:
也就是说我们有GetEnumerator方法才行,虽然编译错误,我们先反编译一下看看foreach最终会编译成什么?
看到了吧,foreach会生成的代码是需要一个方法GetEnumerator方法返回一个枚举器, 改造一下Person类:
public class Person : IEnumerable
{
private string[] Friends = new string[] { "name0", "name2", "name3", "name4" };
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
public string Email
{
get;
set;
}
#region IEnumerable 成员
//这个方法的作用就是返回一个“枚举器”
public IEnumerator GetEnumerator()
{
return new PersonEnumerator(this.Friends);
}
#endregion
}
public class PersonEnumerator : IEnumerator
{
public PersonEnumerator(string[] fs)
{
_friends = fs;
}
private string[] _friends;
//一般下标都是一开始指向了第一条的前一条。
private int index = -1;
#region IEnumerator 成员
//着重说明,这个返回值类型,是我们用foreach中var自动推断出来的类型。之前我反编译过foreach Hashtable的代码看过。
public object Current
{
get
{
if (index >= 0 && index < _friends.Length)
{
return _friends[index];
}
else
{
throw new IndexOutOfRangeException();
}
}
}
public bool MoveNext()
{
if (index + 1 < _friends.Length)
{
index++;
return true;
}
return false;
}
public void Reset()
{
index = -1;
}
#endregion
}
再次运行:
=============================================================
综上所述,写foreach的效果跟下面的代码一样:
Person p = new Person();
IEnumerator etor = p.GetEnumerator();
while (etor.MoveNext())
{
Console.WriteLine(etor.Current.ToString());
}
当然了,如果我们自己写一个枚举器比较麻烦,可利用yield关键字编译器会自动生成,那就改造一下GetEnumerator方法,
public IEnumerator<string> GetEnumerator()
{
for (int i = 0; i < Friends.Length; i++)
{
yield return Friends[i];
}
}
结果跟之前的运行一样,我们反编译一下代码看看:
public class Person
{
private string[] Friends = new string[] { "name0", "name2", "name3", "name4" };
public IEnumerator<string> GetEnumerator()
{
for (int i = 0; i < this.Friends.Length; i++)
{
yield return this.Friends[i];
}
}
public int Age { get; set; }
public string Email { get; set; }
public string Name { get; set; }
[CompilerGenerated]
private sealed class <GetEnumerator>d__0 : IEnumerator<string>, IEnumerator, IDisposable
{
private int <>1__state;
private string <>2__current;
public Program.Person <>4__this;
public int <i>5__1;
[DebuggerHidden]
public <GetEnumerator>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
}
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<i>5__1 = 0;
while (this.<i>5__1 < this.<>4__this.Friends.Length)
{
this.<>2__current = this.<>4__this.Friends[this.<i>5__1];
this.<>1__state = 1;
return true;
Label_0052:
this.<>1__state = -1;
this.<i>5__1++;
}
break;
case 1:
goto Label_0052;
}
return false;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
string IEnumerator<string>.Current =>
this.<>2__current;
object IEnumerator.Current =>
this.<>2__current;
}
}