I have a class called GenericPermutations that is both enumerable and an enumerator. Its job is to take an ordered list of objects and iterate through each permutation of them in order.
Example, an integer implemenation of this class could iterate through the following:
GenericPermutations<int> p = new GenericPermutations<int>({ 1, 2, 3 });
p.nextPermutation(); // 123
p.nextPermutation(); // 132
p.nextPermutation(); // 213
// etc.
So its enumerable in the sense that it contains a 'list' of things you can enumerate over. It's also an enumerator, because its job involves finding the next permutation.
THE ISSUE: I am currently trying to integrate IEnumerator and IEnumerable with this class, and it seems to me like it should be both (rather than using a sub class as the IEnumerable). Thus far I have avoided the issue with trying to get two enumerators from it by passing a new GenericPermutation object in the GetEnumerator
method.
Is this a bad idea? Anything else I should consider?
It is possible for one object to behave as both an
IEnumerator<T>
andIEnumerable<T>
, but it is generally difficult for an object to do so in such fashion as to avoid quirky semantics; unless theIEnumerator<T>
is going to be stateless (e.g. an empty enumerator, whereMoveNext()
always returns false, or an endless-repeat enumerator, whereMoveNext()
does nothing but always returns true, andCurrent
always returns the same value), every call toGetEnumerator()
must return a distinct object instance, and there's likely to be little value in having that instance implementIEnumerable<T>
.Having a value type implement
IEnumerable<T>
andIEnumerator<T>
, and having itsGetEnumerator()
method returnthis
, would satisfy the requirement that each call toGetEnumerator
return a distinct object instance, but having value types implement mutable interfaces is generally dangerous. If a value type is boxed toIEnuerator<T>
and never unboxed, it will behave as a class-type object, but there's no real reason why it shouldn't have simply been a class-type object.Iterators in C# are implemented as class objects which implement both
IEnumerable<T>
andIEnumerator<T>
, but they include a fair bit of fancy logic to ensure semantic correctness. The net effect is that having one object implement both interfaces offers a slight improvement in performance, in exchange for a fair bit of complexity in the generated code, and some semantic quirkiness in theirIDisposable
behavior. I would not recommend this approach in any code that needs to be human-readable; since theIEnumerator<T>
andIEnumerable<T>
aspects of the class mostly use different fields, and since a combined class needs to have a "thread-id" field which wouldn't be needed if using separate classes, the performance improvement one can achieve by using the same object to implement for both interfaces is limited. Worth doing perhaps if adding the complexity to the compiler will provide that slight performance improvement to millions of iterator routines, but not worth doing to improve the performance of one routine.Reduce your confusion (?) by using the generic versions of
IEnumerable
andIEnumerator
.A permutation enumerable is
IEnumerable<IEnumerable<T>>
. So you might have something likeand
Furthermore, I've seen more than one case where a single type implemented both
IEnumerable<T>
andIEnumerator<T>
; its GetEnumerator method was simplyreturn this;
.I think such a type would need to be a struct, though, because if it were a class you'd have all sorts of problems if you called GetEnumerator() a second time before the first enumeration was completed.
EDIT: Consuming the permuter
Assuming the input sequence is { 1, 2, 3 }, the output is
EDIT:
Here's a super-inefficient implementation to illustrate the suggestion: