关于yield IEnumberable的迭代器问题

Chr*_*ris 1 c# ienumerable yield-return

我写了一个程序,旨在从给定的起点创建一个随机的数字列表.这是一个很快的肮脏的东西,但我发现它有一个有趣的效果,我不太明白.

void Main()
{
    List<int> foo = new List<int>(){1,2,3};
    IEnumerable<int> bar = GetNumbers(foo);
    for (int i = 1; i < 3; i++)
    {
        foo = new List<int>(){1,2,3};
        var wibble = GetNumbers(foo);
        bar = bar.Concat(wibble);
    }
    Iterate(bar);
    Iterate(bar);
}

public void Iterate(IEnumerable<int> numbers)
{
    Console.WriteLine("iterating");
    foreach(int number in numbers)
    {
        Console.WriteLine(number);
    }
}

public IEnumerable<int> GetNumbers(List<int> input)
{
    //This function originally did more but this is a cutdown version for testing.
    while (input.Count>0)
    {
        int returnvalue = input[0];
        input.Remove(input[0]);
        yield return returnvalue;
    }
}
Run Code Online (Sandbox Code Playgroud)

运行它的输出是:

iterating
1
2
3
1
2
3
1
2
3
iterating
Run Code Online (Sandbox Code Playgroud)

也就是说我第二次bar在它空了之后立即迭代.

我认为这与我第一次迭代它清空用于生成列表的列表并随后使用这些现在为空以进行迭代的列表这一事实有关.

我的困惑在于为什么会这样?为什么我的IEnumerables每次枚举时都不会从默认状态开始?有人可以解释我到底在做什么吗?

并且要清楚我知道我可以通过添加一个强制立即评估和存储结果的.ToList()调用来解决这个问题GetNumbers().

小智 6

你的迭代器确实从它的初始状态开始.但是,它会修改它正在读取的列表,一旦列表被清除,你的迭代器就没有任何东西要做了.基本上,考虑一下

var list = new List<int> { 1, 2, 3 };
var enumerable = list.Where(i => i != 2);
foreach (var item in enumerable)
    Console.WriteLine(item);
list.Clear();
foreach (var item in enumerable)
    Console.WriteLine(item);
Run Code Online (Sandbox Code Playgroud)

enumerable不会改变list.Clear();,但它给出的结果.