在接口中使用IEnumerator <T>的模式

mck*_*mey 3 c# ienumerable ienumerator design-patterns

我有一个C#类需要IEnumerable<T>在一堆方法中处理一系列items(),所以我不能简单地foreach在一个方法中.我打电话给它.GetEnumerator()并传递它IEnumerator<T>,它很有效,在循环一个序列时给了我所需的灵活性.

现在我想让其他人在这个过程中添加逻辑.最自然的方法是为它们提供一个接受方法的接口IEnumerator<T>.简单,完成,它的工作原理.

但我担心这是一种反模式.他们必须知道IEnumerator<T>已经.MoveNext()调用过,所以他们可以简单地访问.Current.另外,我没有看到IEnumerator<T>在接口中使用的任何先例.

  1. 我不考虑哪些陷阱?
  2. 是否有另一种模式允许我这种相同的有效机制(即我不希望创建/销毁多个副本)而不暴露IEnumerator<T>自身?

更新:正如我在下面的评论中提到的:我想要的是某种通用Stream<T>.我需要能够有效地看到下一个项目(IEnumerator.Current- > .Peek())并使用它(IEnumerator<T>.MoveNext()- > .Pop()).

我用过,IEnumerator<T>因为它明智地符合法案界面.我喜欢在适合时使用常见的BCL类型,但似乎我在滥用这个类型.

所以问题3)是否有适合这种需要的课程?或者我应该创建自己的流,懒惰地执行IEnumerator<T>内部?然后它将被完全封装.我不想使用许多现有的集合,因为它们有内部存储,而我希望存储是IEnumerable<T>iteslf.


好吧,这听起来像是共识是这样做,IEnumerator<T>往往是一个ValueType以及不知道先验状态,IEnumerator<T>通常是一个坏主意传递它.

我听过的最好的建议是创建我自己的类,它会被传递.还有其他建议吗?

Jon*_*eet 6

绝对不应该IEnumerator<T>四处传球.除了其他任何东西,它在某些情况下可能会产生一些非常奇怪的效果.例如,你期望在这里发生什么?

using System;
using System.Collections.Generic;

class Test
{
    static void ShowCurrentAndNext(IEnumerator<int> iterator)        
    {
        Console.WriteLine("ShowCurrentAndNext");
        Console.WriteLine(iterator.Current);
        iterator.MoveNext(); // Let's assume it returns true
        Console.WriteLine(iterator.Current);
    }

    static void Main()
    {
        List<int> list = new List<int> { 1, 2, 3, 4, 5 };
        using (var iterator = list.GetEnumerator())
        {
            iterator.MoveNext(); // Get things going
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

尝试以下几项更改:

using (List<int>.Enumerator iterator = list.GetEnumerator())
Run Code Online (Sandbox Code Playgroud)

using (IEnumerator<int> iterator = list.GetEnumerator())
Run Code Online (Sandbox Code Playgroud)

尝试预测每种情况下的结果:)

现在不可否认,这是一个特别邪恶的例子,但它确实展示了一些与传递可变状态相关的极端案例.我强烈建议您在"中心"方法中执行所有迭代,该方法仅使用当前值调用适当的其他方法.