C # class IEnumerable And IEnumerator simultaneously. What are the issues with this?

I have a GenericPermutations class, which is both an enumerated and an enumerator. Its task is to take an ordered list of objects and iterate over each of their permutations in order.

Example: an integer implementation of this class could do the following:

GenericPermutations<int> p = new GenericPermutations<int>({ 1, 2, 3 }); p.nextPermutation(); // 123 p.nextPermutation(); // 132 p.nextPermutation(); // 213 // etc. 

Thus, it is enumerable in the sense that it contains a β€œlist” of things that you can enumerate. It is also an enumerator because its task is to find the next permutation.

QUESTION:. I'm currently trying to integrate IEnumerator and IEnumerable with this class, and it seems to me that it should be like (instead of using a subclass like IEnumerable). So far, I have avoided this problem by trying to get two counters from it by passing a new GenericPermutation object in the GetEnumerator method.

It is a bad idea? Anything else I should consider?

+6
source share
2 answers

Reduce confusion (?) IEnumerable using common versions of IEnumerable and IEnumerator .

The enumerated permutation is IEnumerable<IEnumerable<T>> . So you might have something like

 IEnumerable<IEnumerable<T>> GetPermutations(IEnumerable<T> sequence) { return new Permuter<T>(sequence); } 

and

 public class Permuter<T> : IEnumerable<IEnumerable<T>> { ... } 

In addition, I saw more than one case where one type implemented both IEnumerable<T> and IEnumerator<T> ; his GetEnumerator method was simply return this; .

I think this type should be a structure, though, because if it were a class, you would have all kinds of problems if you called GetEnumerator () a second time before the first enumeration is completed.

EDIT: using permutation

 var permuter = GetPermutations(sequence); foreach (var permutation in permuter) { foreach (var item in permutation) Console.Write(item + "; "); Console.WriteLine(); } 

Assuming the input sequence is {1, 2, 3}, the output

 1; 2; 3; 1; 3; 2; 2; 1; 3; 2; 3; 1; 3; 1; 2; 3; 2; 1; 

EDIT:

Here's a super-ineffective implementation to illustrate the sentence:

 public class Permuter<T> : IEnumerable<IEnumerable<T>> { private readonly IEnumerable<T> _sequence; public Permuter(IEnumerable<T> sequence) { _sequence = sequence; } public IEnumerator<IEnumerable<T>> GetEnumerator() { foreach(var item in _sequence) { var remaining = _sequence.Except(Enumerable.Repeat(item, 1)); foreach (var permutation in new Permuter<T>(remaining)) yield return Enumerable.Repeat(item, 1).Concat(permutation); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 
+8
source

One object can behave both IEnumerator<T> and IEnumerable<T> , but for an object it is generally difficult to do so in order to avoid fancy semantics; if IEnumerator<T> not considered to be void (for example, an empty enumerator, where MoveNext() always returns false or an infinite repeat counter, where MoveNext() does nothing, but always returns true, and Current always returns the same value), each call to GetEnumerator() should return a separate instance of the object, and it is probably unlikely that this instance will implement IEnumerable<T> .

Having an implementation of the value type IEnumerable<T> and IEnumerator<T> and having the GetEnumerator() return this method returned, satisfies the requirement that each call to GetEnumerator return a separate instance of the object, but using value types, mutable interfaces are usually dangerous. If a value type is marked in the IEnuerator<T> field and never decompressed, it will behave like a class object, but there is no real reason why it should not just be an object of a class type.

C # iterators are implemented as class objects that implement both IEnumerable<T> and IEnumerator<T> , but they include a fair bit of fancy logic to ensure semantic correctness. The net effect is that having one object implements both interfaces, which gives a slight performance improvement, in exchange for a fair bit of complexity in the generated code and some semantic quirkiness in their IDisposable behavior. I would not recommend this approach in any code that should be human readable; since aspects of the IEnumerator<T> and IEnumerable<T> class mainly use different fields, and since the combined class should have a stream-identifier field that would not be needed when using separate classes, you can achieve performance improvements using the same object for implementation for both interfaces, limited. It’s worth doing, perhaps adding complexity to the compiler will provide a slight performance improvement for millions of iterator routines, but not worth it to improve the performance of one routine.

0
source

Source: https://habr.com/ru/post/901070/


All Articles