当我通过比较来制作一个逻辑来迭代两个不同类型的枚举时,我发现了这个:
class Program
{
public static IEnumerable<mm> YieldlyGet()
{
yield return new mm { Int = 0 };
yield return new mm { Int = 1 };
yield return new mm { Int = 2 };
yield return new mm { Int = 3 };
yield return new mm { Int = 4 };
yield return new mm { Int = 5 };
}
public static IEnumerable<int> YieldlyGetInt()
{
yield return 0;
yield return 1;
yield return 2;
yield return 3;
yield return 4;
yield return 5;
}
public static IEnumerable<int> Get()
{
return new List<int> { 0, 1,2,3,4,5 };
}
static void Main(string[] args)
{
var yieldr = YieldlyGet().GetEnumerator();
var yieldv = YieldlyGetInt().GetEnumerator();
var list = Get().GetEnumerator();
int i = -1;
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.WriteLine("For the current index: {0}", ++i);
Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);
Console.ReadLine();
}
问题在于,当我在最后一个位置之后,列表会向我显示默认值,而yield创建的Iterator会一直显示最后一个值。
对于当前指数: 6
y-r:我下次要搬家吗? 错误,如果是,则值: 5
y-v:我下次应该搬家吗? 错误,如果是,则值: 5
我:我下次要搬家吗? 错误,如果是,则值: 0
为什么?
答案 0 :(得分:7)
根据IEnumerator<T>.Current
属性的MSDN文档:
Current
未定义[when]:对MoveNext
的最后一次调用返回false
,表示集合的结束。
这意味着一旦Current
返回MoveNext
,枚举器的底层实现就可以从false
返回任意值。它可以是0,6,-1,2147483647,或者只是随机选取的值;无论如何,你都不应该使用它。
答案 1 :(得分:2)
继承IEnumerator实现。 对于列表,有枚举器的实现描述:
[Serializable]
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
{
private List<T> list;
private int index;
private int version;
private T current;
public T Current
{
get
{
return this.current;
}
}
....
internal Enumerator(List<T> list)
{
this.list = list;
this.index = 0;
this.version = list._version;
this.current = default (T); //there are default of T
}
....
}
对于产量实现是不同的。它返回最后一个值(导致当前未改变电流)。
这种差异可能是由于这段代码是由不同的人在不同的时间写的。此行为未定义,而且,如果您正确使用IEnumerables,您不应该看到此行为(原因通常人们不明确使用Enumerator.Next()但使用foreach,linq或索引直接访问(对于数组)) 。
答案 2 :(得分:2)
让我们来看看List<T>
类使用的枚举器中发生了什么(这是List<int>.Enumerator
)。实际上msdn表示当前值未定义,但我们可以分析Framework 4.0的实现。因此,当枚举器定位在最后一个元素MoveNextRare
之后将被调用:
public bool MoveNext()
{
List<T> ts = this.list;
if (this.version != ts._version || this.index >= ts._size)
{
return this.MoveNextRare();
}
else
{
this.current = ts._items[this.index];
List<T>.Enumerator<T> enumerator = this;
enumerator.index = enumerator.index + 1;
return true;
}
}
因此列表有效(未更改)此方法返回defaut(T)
值(对于int为0
)
private bool MoveNextRare()
{
if (this.version != this.list._version)
trow new InvalidOperationException();
this.index = this.list._size + 1;
this.current = default(T);
return false;
}
生成的枚举器怎么样? C#生成枚举器类,它将为每个yield return
语句提供状态。移至下一个状态会为此枚举器设置Current
值:
bool MoveNext()
{
bool flag;
int state = this.state;
if (state == 0)
{
this.state = -1;
this.current = 0;
this.state = 1;
flag = true;
}
else if (state == 1)
{
this.state = -1;
this.current = 1;
this.state = 2;
flag = true;
}
// ...
else if (state == 5)
{
this.state = -1;
this.current = 5;
this.state = 6;
flag = true;
}
else if (state == 6)
{
this.state = -1;
flag = false;
return flag;
}
else
{
flag = false;
return flag;
}
return flag;
flag = false;
return flag;
}
有趣的是,this.current
在最后一次分配后(状态为5
时)未更改。这就是为什么对Current
的所有后续调用都会返回由最后yield return
次调用设置的值。