以下为什么不工作? (IEnumerable / IEnumerator)

时间:2013-10-23 08:23:27

标签: c# ienumerable enumerate

我在这个MSDN page上尝试了这个例子。我试图更改GetEnumerator方法。我知道有些东西在这方面看起来不对,但它符合并且不会运行。错误是枚举器尚未启动且应该调用MoveNext,但它是被调用!

class Program
{
    static void Main(string[] args)
    { 
        foreach (var day in new DaysOfTheWekk())
        {
            Console.WriteLine(day) ;
        }
        Console.ReadLine();
    }
}

public class DaysOfTheWekk: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public IEnumerator GetEnumerator()
    {
        days.GetEnumerator().MoveNext();
        yield return days.GetEnumerator().Current;
    }
}

5 个答案:

答案 0 :(得分:3)

为什么要调用moveNext?只需忽略.Current

public class DaysOfTheWeek: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public IEnumerator GetEnumerator()
    {
        return days.GetEnumerator();
    }
}

否则使用while循环,因为:

public class DaysOfTheWeek: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public IEnumerator GetEnumerator()
    {
        var enumerator = days.GetEnumerator();
        while(enumerator.MoveNext())
        { 
            yield return enumerator.Current;
        }
    }
}

说明:GetEnumerator()方法总是返回一个新的枚举器,所以如果你调用GetEnumerator().Current,那么在新返回的实例上没有调用MoveNext()函数!请使用我的第二个示例中所述的变量。

答案 1 :(得分:3)

您已在其他枚举器上调用MoveNext()

您的代码等同于

public IEnumerator GetEnumerator()
{
    var enumerator1 = days.GetEnumerator();
    enumerator1.MoveNext();
    var enumerator2 = days.GetEnumerator();
    yield return enumerator2.Current;
}

每次调用GetEnumerator()时,新的枚举器都会受到攻击(至少对于IEnumerable的BCL实现而言),正如您从上面的代码中看到的那样,您构造了两个枚举器并在一个上调用MoveNext和Current在另一。这是属性和方法之间的关键概念差异。应该期望一个方法返回一个操作的结果,而一个属性应该返回相同的值,除非该对象的状态发生了变化。 您的代码中似乎也存在逻辑错误,您只返回第一个元素,如果没有,则会失败,所以基本上您已经实现了.Single()方法 如果你改为

,你的代码就可以了
public IEnumerator GetEnumerator()
{
    var enumerator = days.GetEnumerator();
    while(enumerator.MoveNext()){
       yield return enumerator.Current;
    }
}

当然其功能与

相同
public IEnumerator GetEnumerator()
{
    foreach(var day in days){
       yield return day;
    }
}

答案 2 :(得分:1)

days.GetEnumerator().MoveNext();
yield return days.GetEnumerator().Current;

在这里,您可以创建两个不同的枚举器。您先在 上调用MoveNext,然后在下面的行中创建另一个,然后访问Current

答案 3 :(得分:1)

另一种解决方案是,在调用方法GetEnumerator()时,应确保始终返回相同的Iterator。这是示例实现:

public class DaysOfTheWeek : IEnumerable
{
    private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    private IEnumerator iterator;

    public DaysOfTheWeek()
    {
        iterator = days.GetEnumerator();
        iterator.MoveNext();
    }

    public IEnumerator GetEnumerator()
    {
        return iterator;
    }
}

在构造函数中调用MoveNext()是没有必要的,你必须在iterator.current之前调用iterator.MoveNext()方法。

关键是当你调用GetEnumerator()方法时,你将始终使用相同的迭代器。

答案 4 :(得分:0)

我认为你假设days.GetEnumerator()总是返回相同的枚举器。它有一个很好的理由每次返回一个新的 - 如果只有一个,不同的代码片段无法同时枚举。这不会很好。

拨打days.GetEnumerator()一次,或写下return days.GetEnumerator();