代码之家  ›  专栏  ›  技术社区  ›  Bronumski

当类不实现IEnumerable时,GetEnumerator方法是否仍然是等幂的

  •  2
  • Bronumski  · 技术社区  · 14 年前

    这个问题与另一个问题背道而驰 question 我提出的关于在迭代对象时通过修改对象来滥用IEnumerable接口的问题。

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

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

    编辑(添加上下文)

    为了解决这个问题,我的建议是当遍历队列时,每个项都会在运行时退出队列。此外,在调用GetEnumerator之后推送到队列中的任何新对象仍将被迭代。

    4 回复  |  直到 7 年前
        1
  •  3
  •   Jon Skeet    14 年前

    不是那个 类型 它是等幂的-这甚至没有多大意义;你 可以 意思是不变的,但这还不清楚。是那个 GetEnumerator 方法本身通常是等幂的。

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

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

        2
  •  3
  •   Johannes Rudolph    14 年前

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

    请不要混淆(运行时)Duck类型的概念和滥用支持的编译器 foreach 以支持所需的语义。

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

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

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

    另一方面,这只解释了“源”操作。如果使用枚举数实现筛选或转换操作,则应始终尝试使它们成为等幂。

        3
  •  0
  •   Dan Tao    14 年前

    似乎你想要一个队列类,从中你可以在一个漂亮的一行程序中对所有项目进行出列。

    这个想法本身没什么不对的,我只是怀疑你是否喜欢特别使用 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> .)

    这样你仍然可以得到看起来“干净”的代码,我怀疑你在追求,没有(潜在的)非等幂的混淆 方法 :

    // Pretty clean, right?
    foreach (T item in queue.DequeueAll())
    {
        Console.WriteLine(item);
    }
    
        4
  •  0
  •   supercat    14 年前

    我建议在集合上使用ForEach不应该更改它,除非集合类型的名称暗示这将发生。我认为的问题是,如果执行一个方法将集合消费到可枚举的对象中(例如,允许“MyThing.DequeueAsEnum中的每个Foo”),应该返回什么。如果DequeueAsEnum返回一个iEnumerable,那么有人可能会希望使用“Dim myIEnumerable As iEnumerable=MyThing.DequeueAsEnum”逃脱惩罚,然后在两个不相交的循环中为每个循环使用myIEnumerable。如果DequeueAsEnum返回一个类型EnumerableOnlyOnce,那么它的返回应该只枚举一次就更清楚了。可以肯定的是,新C语言和VB.NET方言中隐式类型的存在使得当某人不应该把函数返回给变量时,有可能更大的可能性,但我不知道如何防止。

    顺便说一句,在很多情况下,防止类引用存储到变量中是有帮助的;是否有任何方法可以声明类,使外部代码可以使用该类类型的表达式,但不能声明其变量?