I have writen about custom IEnumerator
. Whats the simplest way to make IEnumerable
from it ? Ideal solution (one line of code) would be if there was some class for that purpose. Or do I have to create my own ?
In my collection of C# utils I have this:
class Enumerable<T> : IEnumerable<T>
{
Func<IEnumerator<T>> factory;
public Enumerable(Func<IEnumerator<T>> factory) { this.factory = factory; }
public IEnumerator<T> GetEnumerator() { return this.factory(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
This takes an IEnumerator factory function, which usually can be provided very easily instead of the single IEnumerator instance (which yields wrong results after first iteration and breaks the semantics of IEnumerable). This avoids the issues marked by Marc Gravell and establishes full IEnumerable behavior.
I use it this way:
IEnumerable<Fruit> GetFruits()
{
var arg1 = ...
return new Enumerable<Fruit>(() => new FruitIterator(arg1, arg2, ...));
}
I would really approach this the other way around; while you can (as per Mike P's excellent answer) wrap an enumerator to pretend to be enumerable, there are some things that you can't really do - for example, it is hoped (although, to be fair, not insisted) that you can obtain multiple enumerators from an enumerable, ideally isolated and repeatable. So if I do:
Assert.AreEqual(sequence.Sum(), sequence.Sum());
but if you "spoof" the enumerator into an enumerable, the second sequence will be empty. Or if you do them in parallel - just bizarre. And there are methods that process them in parallel - consider:
Assert.IsTrue(sequence.SequenceEqual(sequence));
this works both enumerators forward at the same time, so if you only have one enumerator, you are fairly scuppered.
There is a reset on enumerators, but this is largely a design mistake and shouldn't be used (it is even a formal requirement in the spec that iterator blocks throw an exception if you call it).
A better (IMO) question is "how do I get an enumerator from an enumerable", in which case the answer is "call GetEnumerator(), and remember to check to dispose to iterator" - or in simpler terms "use foreach
".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With