1,最先学会的是,继承了IEnumerable接口的类都可以使用foreach遍历,但一直没有多想。
2,IEnumerable和IEnumerable<out T>的定义:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
3,IEnumerator和IEnumerator<out T>的定义: public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
4,可以这么理解两个接口:可枚举对象返回一个枚举器用于枚举其内部状态。
5,每一次foreach之后,都会重新调用GetEnumerator,foreach语句并不会调用Enumerator的Reset方法;
对于泛型版本的GetEnumerator<T>,foreach同样不会调用Reset方法,但会调用Dispose。
以下是测试代码的输出:
[08.177] StringEnumerator
[08.191] StringMoveNext
[08.191] StringCurrent
Hello
[08.191] StringMoveNext
[08.191] StringCurrent
World
[08.191] StringMoveNext
[08.191] StringCurrent
!
[08.192] StringMoveNext
[08.192] StringEnumerator
[08.192] StringMoveNext
[08.192] StringCurrent
Hello
[08.192] StringMoveNext
[08.192] StringCurrent
World
[08.193] StringMoveNext
[08.193] StringCurrent
!
[08.193] StringMoveNext
[08.195] NumberEnumerator
[08.196] NumberMoveNext
[08.196] NumberCurrent<T>
1
[08.198] NumberMoveNext
[08.199] NumberCurrent<T>
2
[08.199] NumberMoveNext
[08.201] NumberDispose
[08.201] NumberEnumerator
[08.201] NumberMoveNext
[08.202] NumberCurrent<T>
1
[08.202] NumberMoveNext
[08.203] NumberCurrent<T>
2
[08.203] NumberMoveNext
[08.203] NumberDispose
using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleTest
{
public class Program
{
static void Main(string[] args)
{
StringEnumerable se = new StringEnumerable();
foreach (string s in se)
{
Console.WriteLine(s);
}
foreach (string s in se)
{
Console.WriteLine(s);
}
Console.WriteLine();
NumberEnumerable<double> ne = new NumberEnumerable<double>(new double[] { 1.0, 2.0 });
foreach (double d in ne)
{
Console.WriteLine(d);
}
foreach (double d in ne)
{
Console.WriteLine(d);
}
Console.ReadKey();
}
}
class StringEnumerable : IEnumerable
{
private string[] strs = new string[] { "Hello", "World", "!" };
public IEnumerator GetEnumerator()
{
return new StringEnumerator(strs);
}
}
class StringEnumerator : IEnumerator
{
private string[] strs;
private int index;
private object current;
public StringEnumerator(string[] strs)
{
Console.WriteLine("[{0}] StringEnumerator", DateTime.Now.ToString("ss.fff"));
this.strs = strs;
index = -1;
}
public object Current
{
get
{
Console.WriteLine("[{0}] StringCurrent", DateTime.Now.ToString("ss.fff"));
return current;
}
}
public bool MoveNext()
{
Console.WriteLine("[{0}] StringMoveNext", DateTime.Now.ToString("ss.fff"));
while (++index < strs.Length)
{
current = strs[index];
return true;
}
return false;
}
public void Reset()
{
Console.WriteLine("[{0}] StringReset", DateTime.Now.ToString("ss.fff"));
index = -1;
}
}
class NumberEnumerable<T> : IEnumerable<T>
{
private T[] ts;
public NumberEnumerable(T[] ts)
{
this.ts = ts;
}
IEnumerator IEnumerable.GetEnumerator()
{
return new NumberEnumerator<T>(ts);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return new NumberEnumerator<T>(ts);
}
}
class NumberEnumerator<T> : IEnumerator<T>
{
private T[] strs;
private int index;
private T current;
public NumberEnumerator(T[] strs)
{
Console.WriteLine("[{0}] NumberEnumerator", DateTime.Now.ToString("ss.fff"));
this.strs = strs;
index = -1;
}
object IEnumerator.Current
{
get
{
Console.WriteLine("[{0}] NumberCurrent", DateTime.Now.ToString("ss.fff"));
return current;
}
}
T IEnumerator<T>.Current
{
get
{
Console.WriteLine("[{0}] NumberCurrent<T>", DateTime.Now.ToString("ss.fff"));
return current;
}
}
public bool MoveNext()
{
Console.WriteLine("[{0}] NumberMoveNext", DateTime.Now.ToString("ss.fff"));
while (++index < strs.Length)
{
current = strs[index];
return true;
}
return false;
}
public void Reset()
{
Console.WriteLine("[{0}] NumberReset", DateTime.Now.ToString("ss.fff"));
index = -1;
}
public void Dispose()
{
Console.WriteLine("[{0}] NumberDispose", DateTime.Now.ToString("ss.fff"));
index = -1;
}
}
}
6,foreach默认可枚举对象的GetEnumerator方法,其他需要进行枚举的方法需要返回IEnumerable或者IEnumerable<T>,以供foreach调用其GetEnumerator方法。
7,yield语句只能使用在返回IEnumerable或者IEnumerator接口的方法中。
yield语句会被编译为实现了IEnumerator<T>的内部枚举器。
以下是代码使用.NET Reflector 7.0反编译出来的结果:
using System;
using System.Collections;
using System.Collections.Generic;
namespace YieldTest
{
class Program
{
static void Main(string[] args)
{
YieldString ys = new YieldString();
foreach (string s in ys)
{
Console.WriteLine(s);
}
foreach (string s in ys.Next())
{
Console.WriteLine(s);
}
Console.ReadKey();
}
}
class YieldString
{
private string[] strs = new string[] { "Hello", "World", "!" };
public IEnumerator GetEnumerator()
{
foreach (string s in strs)
{
yield return s;
}
}
public IEnumerable Next()
{
foreach (string s in strs)
{
yield return s;
}
}
}
}
internal class YieldString
{
// Fields
private string[] strs = new string[] { "Hello", "World", "!" };
// Methods
public IEnumerator GetEnumerator()
{
foreach (string iteratorVariable0 in this.strs)
{
yield return iteratorVariable0;
}
}
public IEnumerable Next()
{
foreach (string iteratorVariable0 in this.strs)
{
yield return iteratorVariable0;
}
}
// Nested Types
[CompilerGenerated]
private sealed class <GetEnumerator>d__0 : IEnumerator<object>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private object <>2__current;
public YieldString <>4__this;
public string[] <>7__wrap3;
public int <>7__wrap4;
public string <s>5__1;
// Methods
[DebuggerHidden]
public <GetEnumerator>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
}
private void <>m__Finally2()
{
this.<>1__state = -1;
}
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>1__state = 1;
this.<>7__wrap3 = this.<>4__this.strs;
this.<>7__wrap4 = 0;
while (this.<>7__wrap4 < this.<>7__wrap3.Length)
{
this.<s>5__1 = this.<>7__wrap3[this.<>7__wrap4];
this.<>2__current = this.<s>5__1;
this.<>1__state = 2;
return true;
Label_0079:
this.<>1__state = 1;
this.<>7__wrap4++;
}
this.<>m__Finally2();
break;
case 2:
goto Label_0079;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
case 2:
this.<>m__Finally2();
break;
}
}
// Properties
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
}
[CompilerGenerated]
private sealed class <Next>d__6 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private object <>2__current;
public YieldString <>4__this;
public string[] <>7__wrap9;
public int <>7__wrapa;
private int <>l__initialThreadId;
public string <s>5__7;
// Methods
[DebuggerHidden]
public <Next>d__6(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
private void <>m__Finally8()
{
this.<>1__state = -1;
}
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>1__state = 1;
this.<>7__wrap9 = this.<>4__this.strs;
this.<>7__wrapa = 0;
while (this.<>7__wrapa < this.<>7__wrap9.Length)
{
this.<s>5__7 = this.<>7__wrap9[this.<>7__wrapa];
this.<>2__current = this.<s>5__7;
this.<>1__state = 2;
return true;
Label_0079:
this.<>1__state = 1;
this.<>7__wrapa++;
}
this.<>m__Finally8();
break;
case 2:
goto Label_0079;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
[DebuggerHidden]
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
{
this.<>1__state = 0;
return this;
}
return new YieldString.<Next>d__6(0) { <>4__this = this.<>4__this };
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
case 2:
this.<>m__Finally8();
break;
}
}
// Properties
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
}
}