English 中文(简体)
当类没有实现IEnumerable时,GetEnumerator方法是否仍然是幂等的
原标题:Should a GetEnumerator method still be idempotent when the class does not implement IEnumerable

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

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

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

EDIT(添加上下文)

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

最佳回答

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

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

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

问题回答

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

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

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

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

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

另一方面,这只说明了“源”操作。如果您使用枚举器来实现过滤或转换操作,则应始终尝试使它们成为幂等运算。

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

这个想法本身没有错;我只是质疑您是否喜欢专门使用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);
}

我建议在集合上使用ForEach不应该更改它,除非集合类型的名称暗示这将发生。我脑海中的问题是,如果执行一个方法将集合消费为可枚举的东西,应该返回什么(例如,允许“For Each Foo in MyThing.DequeueAsEnum”)。如果DequeueAsEnm返回一个iEnumerable,那么有人可能会逃脱“Dim myIEnumerable As iEnumerable=MyThing.DqueueAsEnu”的惩罚,然后在两个不相交的For Each循环中使用MyIEnumeraable。如果DequeueAsEnum返回类型EnumerableOnlyOnce,那么它的返回应该只枚举一次就更清楚了。可以肯定的是,在较新的C#和VB.Net方言中,隐式键入的存在使人们更有可能在不应该将函数return分配给变量的时候将其分配给变量,但我不知道如何防止这种情况发生。

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





相关问题
Manually implementing high performance algorithms in .NET

As a learning experience I recently tried implementing Quicksort with 3 way partitioning in C#. Apart from needing to add an extra range check on the left/right variables before the recursive call, ...

Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

How do I compare two decimals to 10 decimal places?

I m using decimal type (.net), and I want to see if two numbers are equal. But I only want to be accurate to 10 decimal places. For example take these three numbers. I want them all to be equal. 0....

Exception practices when creating a SynchronizationContext?

I m creating an STA version of the SynchronizationContext for use in Windows Workflow 4.0. I m wondering what to do about exceptions when Post-ing callbacks. The SynchronizationContext can be used ...

Show running instance in single instance application

I am building an application with C#. I managed to turn this into a single instance application by checking if the same process is already running. Process[] pname = Process.GetProcessesByName("...

How to combine DataTrigger and EventTrigger?

NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger? I think I need to combine an EventTrigger and a DataTrigger to achieve what I m after: when ...