开发者

IEnumerable from IEnumerator

I have writen about custom IEnumerator. Whats the simplest way to make IEnumerable from it ? Ideal solution (one line of code) wou开发者_运维技巧ld be if there was some class for that purpose. Or do I have to create my own ?


There's no built-in method, unfortunately. I have this extension method that I use often enough:

static IEnumerable Iterate(this IEnumerator iterator)
{
while (iterator.MoveNext())
    yield return iterator.Current;
}


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


Pretty simple:

class Enumerate : IEnumerable
{
    private Enumerate IEnumerator it;
    public Enumerate(IEnumerator it) { this.it = it; }
    public IEnumerator GetEnumerator() { return this.it; }
}

This also allows the user to call IEnumerator.Reset() if the enumerator you gave it supports it.


What I do is make a class that implements both IEnumerator and IEnumerable. Make GetEnumerator() return itself and you can iterate it like normal.

public class MyClassEnumerator : IEnumerator<MyClass>, IEnumerable<MyClass>
{
    public MyClass Current { get; private set; }

    object IEnumerator.Current => Current;

    public void Reset()
    {
        ...
    }

    public bool MoveNext()
    {
        ...
    }

    public IEnumerator<MyClass> GetEnumerator()
    {
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public void Dispose()
    {
        ...
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜