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

时间:2010-07-06 18:10:35

标签: c# design-patterns ienumerable ienumerator

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

现在我想让其他人在这个过程中添加逻辑。最自然的方法是为他们提供一个接受IEnumerator<T>的方法的接口。简单,完成,并且有效。

但我担心这是反模式。他们必须知道IEnumerator<T>已经调用.MoveNext(),因此他们只需访问.Current即可。另外,我没有看到在要实现的接口中使用IEnumerator<T>的任何先例。

  1. 我不考虑哪些陷阱?
  2. 是否有其他模式允许我使用相同的高效机制(即我不希望创建/销毁多个副本)而不暴露IEnumerator<T>本身?
  3. 更新:正如我在下面的评论中提到的:我想要的是某种通用Stream<T>。我需要能够有效地查看下一个项目(IEnumerator.Current - &gt; .Peek())并使用它(IEnumerator<T>.MoveNext() - &gt; .Pop())。

    我使用了IEnumerator<T>,因为它明智地适合了账单界面。我喜欢在适合时使用常见的BCL类型,但似乎我在滥用这个类型。

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


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

    我听过的最好的建议就是创建我自己的课程。还有其他建议吗?

4 个答案:

答案 0 :(得分: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);
        }
    }
}

尝试进行一些更改:

using (List<int>.Enumerator iterator = list.GetEnumerator())

using (IEnumerator<int> iterator = list.GetEnumerator())

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

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

答案 1 :(得分:2)

我强烈建议不要通过调查员本身;除了需要现值之外,你有什么理由呢?

除非我遗漏了一些明显的东西,否则我建议你的实用程序函数只是将你枚举的类型作为参数,然后有一个外部foreach循环来处理实际的枚举。

也许你可以提供一些额外的信息,说明你到目前为止做出这个设计决定的原因。

答案 2 :(得分:2)

听起来像您可能会因使用事件而受益,因此您可以将要处理的项目的通知推送给听众。常规.NET事件按订阅顺序处理,因此如果需要订购,您可能会采用更明确的方法。

您可能还想查看Reactive Framework。

答案 3 :(得分:1)

如果我理解正确,你有很多方法可以在序列上调用MoveNext,你希望这些方法相互合作,所以你传递IEnumerator<T>。正如你所提到的,这里肯定有一些紧耦合,因为你希望枚举器在每个方法的入口处处于特定状态。这听起来像你在这里真正的东西就像Stream类,它既是一个集合(一种)和一个迭代器(有一个当前位置的概念)。我会在你自己的类中包装你的迭代和你需要的任何其他状态,并将各种方法作为该类的成员