为什么为命名迭代器返回IEnumerable?

时间:2014-02-13 08:09:29

标签: c#

(C#新手) 有人可以解释,为什么命名迭代器(我在MS C编程指南中找到的术语)应该返回IEnumerable

// Implementing the enumerable pattern
public System.Collections.IEnumerable SampleIterator(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        yield return i;
    }
}

当类实现GetEnumerator()IEnumerable接口的IEnumerable<T>时,这是有道理的,但我不确定它为什么以及如何对方法和属性起作用。

编辑:有人指出我的问题不是很好。(也许,我仍然在思考C ++) 澄清:为了在某个类的对象上使用foreach循环,类需要实现IEnumerableIEnumerator接口或yield return(即编译器处理所有内容)。 例如:

class ListClass : System.Collections.IEnumerable
{
    public System.Collections.IEnumerator GetEnumerator() {...//uses yield return }  
    ....
}
    ....
    ListClass listClass1 = new ListClass(); // class implements IEnumerable interface method 
    foreach (int i in listClass1) {...}

在上面的示例中,GetEnumerator()已实现,如果您希望对象在foreach循环中运行,据我所知,必须实现GetEnumerator()。 但是,当实现了命名迭代器(上面的代码)时,IEnumberable<T>不是必需的,并且只有{{1}}(或非泛型)必须由方法/属性返回。
希望这能澄清我的问题。

1 个答案:

答案 0 :(得分:4)

实际上,在这种情况下,我会说它应该返回IEnumerable<T> - 或IEnumerable<int>。但原因是因为它起作用,并且因为迭代器块(具有yield语法的方法)可以为您实现该模式。如果您想执行与foreach一起使用的 else ,则必须编写自己的CustomEnumerable / CustomIterator,并手动实现状态机迭代器使用MoveNext() / Current模式,记住在finally中放置任何Dispose()等。很多工作;收获不多。


编辑:这是一个编写得很糟糕的,可能是错误的实现方法,即手工返回自定义迭代器。请注意,返回int[]List<int>非常简单,但会显着改变语义:它不再是假脱机迭代器,这是yield创建的:

static CustomEnumerable SampleIteratorHard(int start, int end)
{
    return new CustomEnumerable(start, end);
}
class CustomEnumerable
{
    private readonly int start, end;
    public CustomEnumerable(int start, int end)
    {
        this.start = start;
        this.end = end;
    }
    public CustomIterator GetEnumerator()
    {
        return new CustomIterator(start, end);
    }
}
class CustomIterator
{
    private readonly int start, end;
    int i, state, value;
    public CustomIterator(int start, int end)
    {
        this.start = start;
        this.end = end;
        start = 0;
    }
    public bool MoveNext()
    {
        if (state == 2) return false;
        if (state == 0)
        {
            i = start - 1; // to leave space for the ++
            state = 1;
        }
        if (state == 1)
        {
            i++;
            if (i <= end)
            {
                value = i;
                return true;
            }
        }
        state = 2;
        return false;
    }
    public int Current {  get {  return value; } }
}