I noticed that List<T>
defines its enumerator as a struct
, while ArrayList
defines its enumerator as a class
. What's the difference? If I am to write an enumerator for my class, which one would be preferable?
EDIT: My requirements cannot be fulfilled using yield
, so I'm implementing an enumerator of my own. That said, I wonder whether it would be better to follow the lines of List<T>
and implement it as a struct.
Like this others, I would choose a class. Mutable structs are nasty. (And as Jared suggests, I'd use an iterator block. Hand-coding an enumerator is fiddly to get right.)
See this thread for an example of the list enumerator being a mutable struct causing problems...
The easiest way to write an enumerator in C# is with the "yield return" pattern. For example.
public IEnumerator<int> Example() {
yield return 1;
yield return 2;
}
This pattern will generate all of the enumerator code under the hood. This takes the decision out of your hands.
Reason List uses a struct enumerator is to prevent garbage generation in foreach statements. This is pretty good reason especially if you are programming for Compact Framework, because CF doesn't have generational GC and CF is usually used on low performance hardware where it can quickly lead to performance issues.
Also, I don't think mutable structs are source of problems in examples some posted, but programmers that don't have good understanding of how value types work.
An enumerator is inherently a changing structure, since it needs to update internal state to move on to the next value in the original collection.
In my opinion, structs should be immutable, so I would use a class.
Write it using yield return
.
As to why you might otherwise choose between class
or struct
, if you make it a struct
then it gets boxed as soon as it is returned as an interface, so making it a struct
just causes additional copying to take place. Can't see the point of that!
Any implementation of IEnumerable<T>
should return a class. It may be useful for performance reasons to have a GetEnumerator
method which returns a struct which provides the methods necessary for enumeration but does not implement IEnumerator<T>
; this method should be different from IEnumerable<T>.GetEnumerator
, which should then be implemented explicitly.
Using this approach will allow for enhanced performance when the class is enumerated using a foreach or "For Each" loop in C# or vb.net or any context where the code which is doing the enumeration will know that the enumerator is a struct, but avoid the pitfalls that would otherwise occur when the enumerator gets boxed and passed by value.
To expand on @Earwicker: you're usually better off not writing an enumerator type, and instead using yield return
to have the compiler write it for you. This is because there are a number of important subtleties that you might miss if you do it yourself.
See SO question "What is the yield keyword used for in C#?" for some more details on how to use it.
Also Raymond Chen has a series of blog posts ("The implementation of iterators in C# and its consequences": parts 1, 2, 3, and 4) that show you how to implement an iterator properly without yield return
, which shows just how complex it is, and why you should just use yield return
.
来源:https://stackoverflow.com/questions/384511/enumerator-implementation-use-struct-or-class