2

这个问题背负了我提出的另一个问题,即通过在迭代对象时修改对象来滥用 IEnumerable 接口。

普遍的共识是任何实现 IEnumerable 的东西都不应该是幂等的。但是 .net 支持使用 foreach 语句进行编译时鸭子类型。任何提供 IEnumerator GetEnumerator() 方法的对象都可以在 foreach 语句中使用。

那么GetEnumerator方法应该是幂等的还是实现IEnumerable的时候呢?

编辑(添加上下文)

为了说明这一点,我建议的是,在遍历队列时,每个项目都会随着它的进行而出队。此外,在调用 GetEnumerator 之后推入队列的任何新对象仍将被迭代。

4

4 回答 4

3

这不是幂等的类型——这甚至没有多大意义;你的意思可能是不可变的,但这并不清楚。GetEnumerator方法本身通常是幂等的。

虽然我会说这通常是GetEnumerator这种情况,但我可以设想一些特殊情况,在这些情况下使用非幂等方法是有意义的。例如,您可能拥有只能读取一次的数据(因为它是从不会再次为相同请求提供服务的 Web 服务器流式传输的,或者类似的东西)。在这种情况下,GetEnumerator必须有效地使数据源无效,以便将来的调用会引发异常。

当然,这些类型和方法应该非常仔细地记录下来,但我认为它们是合理的。

于 2010-11-16T14:06:38.433 回答
3

这个讨论是一个古老的讨论,据我所知,没有共同的共识。

请不要将(运行时)Duck-Typing 的概念与滥用编译器支持foreach以支持您所需的语义相混淆。

您似乎混淆的另一个概念是幂等性与不变性。根据您的措辞,您尝试描述第二个,这意味着提供枚举器的对象在枚举期间被修改。另一方面,幂等性意味着您的枚举器在调用两次时将产生相同的结果。

现在我们已经清楚了这一点,您需要仔细决定您的 IEnumerable 操作应该支持的语义。某些类型的枚举很难实现幂等(即涉及缓存),并且通常属于以下类别之一:

  • 枚举随机变化的数据(即随机数生成器、传感器流)
  • 枚举共享状态(例如文件、数据库、流等)

另一方面,这仅考虑“源”操作。如果您使用枚举器实现过滤器或转换操作,则应始终尝试使它们具有幂等性。

于 2010-11-16T14:15:52.673 回答
0

似乎您想要一个队列类,您可以从中将所有项目从一个漂亮的单行中出列。

这个想法本身并没有错。我只是质疑您是否偏爱专门用于GetEnumerator实现您所追求的目标。

为什么不简单地编写一个更明确的方法呢?例如DequeueAll,,或类似的东西。

例子:

// Just a simplistic example. Not the way I'd actually write it.
class CustomQueue<T> : Queue<T>
{
    public IEnumerable<T> DequeueAll()
    {
        while (Count > 0)
        {
            yield return Dequeue();
        }
    }
}

(请注意,上面的方法甚至可以是扩展方法,如果它从字面上代表您想要的唯一功能,超出 已经提供的功能Queue<T>。)

这样,您仍然可以获得我怀疑您所追求的“干净”的代码,而没有非幂等的(潜在)混淆GetEnumerator

// Pretty clean, right?
foreach (T item in queue.DequeueAll())
{
    Console.WriteLine(item);
}
于 2010-11-16T14:56:24.183 回答
0

我建议在集合上使用 ForEach 不应该更改它,除非集合类型的名称暗示会发生这种情况。我心中的问题是,如果执行一种方法来将集合消耗为可枚举的东西(例如,允许“For Each Foo in MyThing.DequeueAsEnum”),应该返回什么。如果 DequeueAsEnum 返回一个 iEnumerable,那么有人可能希望摆脱“Dim myIEnumerable As IEnumerable = MyThing.DequeueAsEnum”,然后在两个不相交的 For-Each 循环中使用 MyIEnumerable。如果 DequeueAsEnum 返回一个 EnumerableOnlyOnce 类型,那么它的返回应该只枚举一次会更清楚一些。可以肯定的是,在较新的 C# 和 VB 中存在隐式类型。

顺便说一句,在许多情况下,防止将类引用存储到变量中会有所帮助;有没有办法声明一个类,使得外部代码可以使用该类类型的表达式,但不能声明它的变量?

于 2010-11-27T18:23:15.377 回答