当类未实现 IEnumerable 时,GetEnumerator 方法是否仍应具有幂等性

发布于 2024-10-02 07:59:58 字数 478 浏览 8 评论 0原文

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

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

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

编辑(添加上下文)

为了围绕这个问题提供一些上下文,我建议的是,当迭代队列时,每个项目都会被出队。此外,在调用 GetEnumerator 后推送到队列中的任何新对象仍将被迭代。

This question piggy backs of another question which I raised regarding abusing the IEnumerable interface by modifying an object as you iterate over it.

The general consensus is that no anything that Implements IEnumerable should be idempotent. But .net supports compile time duck typing with the foreach statement. Any object that provides an IEnumerator GetEnumerator() method can be used inside a foreach statement.

So should the GetEnumerator method be idempotent or is it when it implements IEnumerable?

EDIT (Added context)

To put some context round this what I am suggesting is that when iterating over a queue each item is dequeued as it goes. Additionally any new objects pushed onto the queue after the call to GetEnumerator would still be iterated over.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

痴情 2024-10-09 07:59:58

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

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

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

It's not the type which is idempotent - that doesn't even make much sense; you may mean immutable, but that's not clear. It's the GetEnumerator method itself which is typically idempotent.

While I'd say that's typically the case, I can envisage special cases where it makes sense to have a non-idempotent GetEnumerator method. For example, it could be that you've got data which can only be read once (because it's streaming from a web server which won't service the same request again, or something like that). In that case, GetEnumerator would have to effectively invalidate the data source, so that future calls would throw an exception.

Such types and methods should be documented very carefully, of course, but I think they're reasonable.

北城挽邺 2024-10-09 07:59:58

这个讨论是一个老话题了,据我所知,还没有达成共识。

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

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

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

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

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

This discussion is an old one and to my knowledge there's no common consensus.

Please do not confuse the concept of (runtime) Duck-Typing with abusing the compiler supported foreach to support your desired semantics.

Another concept you seem to confuse is Idempotence vs. Immutability. According to your wording you try to describe the second, which means the object providing the enumerator gets modified during enumeration. Idempotence on the other hand means your enumerator, when called twice will yield the same results.

Now that we're clear on this, you need to carefully decide on the semantics your IEnumerable operation should support. Certain kind of enumerations are hard to make idempotent (i.e. involve caching), and do usually fall into one of the following categories:

  • Enumerating over randomly changing
    data (i.e. a random number generator, sensor streams)
  • Enumerating over shared state
    (e.g. files, databases, streams etc.)

On the other hand, this only accounts for "source" operations. If you are implementing filter or transformation operations using enumerators, you should always try to make them idempotent.

乄_柒ぐ汐 2024-10-09 07:59:58

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

这个想法本身并没有什么问题;我只是质疑您是否愿意专门使用 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。)

通过这种方式,您仍然可以获得我怀疑您正在追求的“干净”的代码,而不会出现非幂等 GetEnumerator 的(潜在的)混乱:

// Pretty clean, right?
foreach (T item in queue.DequeueAll())
{
    Console.WriteLine(item);
}

It seems you want a queue class from which you can dequeue all items in a nice one-liner.

There's nothing wrong with this idea per se; I'd just question your preference to specifically use GetEnumerator to achieve what you're after.

Why not simply write a method that is more explicit in terms of what it does? For example, DequeueAll, or something of the sort.

Example:

// 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();
        }
    }
}

(Note that the above could even be an extension method, if it represents literally the only functionality you'd want above and beyond what is already provided by Queue<T>.)

This way you could still get the "clean"-looking code I suspect you're after, without the (potential) confusion of a non-idempotent GetEnumerator:

// Pretty clean, right?
foreach (T item in queue.DequeueAll())
{
    Console.WriteLine(item);
}
罪#恶を代价 2024-10-09 07:59:58

我建议在集合上使用 ForEach 不应更改它,除非集合类型的名称暗示将发生这种情况。我心中的问题是,如果执行一个方法将集合消耗为可枚举的内容(例如允许“For Each Foo in MyThing.DequeueAsEnum”),则应该返回什么。如果 DequeueAsEnum 返回一个 iEnumerable,那么有人可能会希望摆脱“Dim myIEnumerable As IEnumerable = MyThing.DequeueAsEnum”,然后在两个不相交的 For-Each 循环中使用 MyIEnumerable。如果 DequeueAsEnum 返回一个类型 EnumerableOnlyOnce,那么它的返回值应该只枚举一次会更清楚。可以肯定的是,较新的 C# 和 VB.Net 方言中隐式类型的存在使得有人更有可能将函数返回值分配给变量,而他们不应该这样做,但我不知道如何防止这种情况发生。

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

I would suggest that using ForEach on a collection shouldn't change it unless the name of the collection type implies that's going to happen. The issue in my mind would be what should be returned if a method is performed to consume a collection into something enumerable (e.g. to allow "For Each Foo in MyThing.DequeueAsEnum"). If DequeueAsEnum returns an iEnumerable, then someone could expect to get away with "Dim myIEnumerable As IEnumerable = MyThing.DequeueAsEnum" and then use MyIEnumerable in two disjoint For-Each loops. If DequeueAsEnum returns a type EnumerableOnlyOnce, then it would be a little clearer that its return should only be enumerated once. To be sure, the existence of implicit typing in newer C# and VB.Net dialects makes it a bit more likely that someone might assign the function return to a variable when they shouldn't, but I don't know how to prevent that.

BTW, there are a number of circumstances where it would be helpful to prevent a class reference from being stored into a variable; is there any way to declare a class in such a way that outside code can use expressions of that class type, but cannot declare variables of it?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文