foreach-本质
为什么可以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; } }