【问题标题】:Iterate over IEnumerable stops after first iteration在第一次迭代后迭代 IEnumerable 停止
【发布时间】:2015-05-19 08:32:07
【问题描述】:

我的Main() 方法中有以下内容

Reeks r = new Reeks();

foreach(int i in r){
    if(i < 1000){
        Console.WriteLine(i);
    }
}

我想要的是遍历Reeks 直到i &gt; 1000 的值,其中i 是前一个值乘以2,即[1,2,4,8,16,32]

Reeks 类看起来像这样

class Reeks : IEnumerable<int>, IEnumerator<int>
{
    private List<int> reeks;
    private int idx = -1;

    public Reeks()
    {
        reeks = new List<int>() {1};
    }

    public IEnumerator<int> GetEnumerator()
    {
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public void Dispose()
    {

    }

    public bool MoveNext()
    {
        if (idx < reeks.Count - 1)
        {
            idx++;
            return true;
        }
        return false;
    }

    public void Reset()
    {
        idx = -1;
    }

    public int Current
    {
        get
        {
            if (idx == -1)
            {
                throw new InvalidOperationException("Enumeration has not started. Call MoveNext");
            }
            else
            {
                if (idx != 0)
                {
                    reeks.Add((reeks[(idx-1)]*2));
                }
                return reeks[idx];
            }
        }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }
}

问题在于它在第一次迭代后停止迭代,它只是在开头打印1,而不是之后应该出现的值。

【问题讨论】:

  • if (idx &lt; reeks.Count - 1)MoveNext 中失败。这是一个家庭作业,你需要手动实现一个迭代器吗?否则,您可以轻松使用迭代器块。
  • @SriramSakthivel 不幸的是
  • 在 Current 中添加到列表是非常危险的,不应该这样做。改为在 MoveNext 中执行!!!
  • 你正在为家庭作业实施这些东西吗?哇——我见过的最没用的作业——这只适用于 .net/C#,而且它已经过时了(抱歉,它已经过时了,但我真的很想知道为什么你希望你的学生知道这些东西而不是对它有好感更一般的编程概念)
  • @CarstenKönig 我不同意。弄清楚迭代器是如何工作的可能是一个很好的练习。了解某事是如何工作的往往比仅仅假设它做了它所做的更好。

标签: c# ienumerable ienumerator


【解决方案1】:

不知道为什么你需要List&lt;T&gt;MoveNext 中的 if (idx &lt; reeks.Count - 1) 在第二次迭代中失败。

如果不需要实现您的自定义迭代器,我会使用迭代器块来实现。

private static IEnumerable<int> Reeks()
{
    int num = 1;
    yield return num;

    while (true)
    {
        num = num * 2;
        yield return num;
    }
}

然后将其用作

foreach (int i in Reeks().TakeWhile(x => x < 1000))
{
    Console.WriteLine(i);
}

正如您在 cmets 中所说,这必须是自定义迭代器。手写的迭代器块看起来像这样。请注意 List&lt;T&gt; 已删除,您不需要列表。您只需要知道最后返回的值是什么。

class Reeks : IEnumerable<int>, IEnumerator<int>
{
    private int current;
    private int idx = -1;

    public IEnumerator<int> GetEnumerator()
    {
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public void Dispose()
    {
        Reset();
    }

    public bool MoveNext()
    {
        if (idx == -1)
        {
            idx = 0;
            current = 1;
        }
        else
        {
            current = current * 2;
        }

        return true;
    }

    public void Reset()
    {
        idx = -1;
    }

    public int Current
    {
        get
        {
            if (idx == -1)
                throw new InvalidOperationException("Enumeration has not started. Call MoveNext");

            return current;
        }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }
}

【讨论】:

    【解决方案2】:

    嗯,这很容易。您的 List 只有一个元素。当迭代Reeks(因此调用MoveNext())时,您将idx 增加1。仍然可以。

    但是,您的 MoveNext() 被定义为

    public bool MoveNext()
    {
        if (idx < reeks.Count - 1)
        {
            idx++;
            return true;
        }
        return false;
    }
    

    让我们遍历前两个;

    迭代 1

    idx = -1;
    reeks.Count = 1; //Because reeks is a List containing 1 element
    
    if(-1 < 0)
    {
        idx++;
        return true;
    }
    

    哪个有效。

    迭代 2

    idx = 0; //incremented -1 by 1
    reeks.Count = 1; //Not altered
    
    if(0 < 0)
    {
        //Not reached!
    }
    
    return false;
    

    这将返回 false。

    编辑

    Sriram 提供的答案可能是最好和最优雅的。但是,如果您必须坚持使用当前拥有的代码,您确实可以在 MoveNext() 方法中添加下一个元素。这也将使它无限。

    public bool MoveNext()
    {
        int size = reeks.Count - 1;
        if (idx < size)
        {
            idx++;
            reeks.Add(reeks[size]*2);
            return true;
        }
        return false;
    }
    

    【讨论】:

    • 关于如何修复的任何建议?调用MoveNext 时是否必须在列表中添加下一个元素?因为它应该是int的无限系列
    猜你喜欢
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 2023-03-11
    • 2021-07-28
    相关资源
    最近更新 更多