Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable from IEnumerator

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 ?

like image 505
Rasto Avatar asked Jan 21 '11 21:01

Rasto


2 Answers

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, ...));
}
like image 103
citykid Avatar answered Oct 08 '22 08:10

citykid


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".

like image 37
Marc Gravell Avatar answered Oct 08 '22 07:10

Marc Gravell