tags:

views:

422

answers:

4

Is there a built-in way to convert IEnumerator<T> to IEnumerable<T>?

+1  A: 

Nope, IEnumerator<> and IEnumerable<> are different beasts entirely.

Jason Watts
+2  A: 

As Jason Watts said -- no, not directly.

If you really want to, you could loop through the IEnumerator<T>, putting the items into a List<T>, and return that, but I'm guessing that's not what you're looking to do.

The basic reason you can't go that direction (IEnumerator<T> to a IEnumerable<T>) is that IEnumerable<T> represents a set that can be enumerated, but IEnumerator<T> is a specific enumeratation over a set of items -- you can't turn the specific instance back into the thing that created it.

Jonathan
I was looking to do something like this in a Skip implementation, the built in Linq Skip is a complete Dog
Sam Saffron
See: http://stackoverflow.com/questions/1018407/what-is-the-most-elegant-way-to-get-a-set-of-items-by-index-from-a-collection/1025137#1025137 ... I was looking for a cleaner way to implement Skip (so its not called on IEnumerator), but it involved IEnumerable wrapping
Sam Saffron
+1  A: 

You could use the following which will kinda work.

public FakeEnumerable<T> : public IEnumerable<T> {
  private IEnumerator<T> m_enumerator;
  public FakeEnumerable(IEnumerator<T> e) {
    m_enumerator = e;
  }
  public IEnumerator<T> GetEnumerator() { 
    return m_enumerator;
  }
  // Rest omitted 
}

This will get you into trouble though when people expect successive calls to GetEnumerator to return different enumerators vs. the same one. But if it's a one time only use in a very constrained scenario, this could unblock you.

I do suggest though you try and not do this because I think eventually it will come back to haunt you.

A safer option is along the lines Jonathan suggested. You can expend the enumerator and create a List<T> of the remaining items.

public static List<T> SaveRest<T>(this IEnumerator<T> enumerator) {
  var list = new List<T>();
  while ( e.MoveNext() ) {
    list.Add(e.Current);
  }
  return list;
}
JaredPar
This will work so long as you only call GetEnumerator() once -- most things that call that expect to get their own copy. Ie. once you've moved through the whole thing, you can't start over again.
Jonathan
@Jonathan, yes entirely true (noted in the answer). Precisely why I said "kinda"
JaredPar
Yeah I did something like this ... another problem with this is that when you get an enumerator its in position -1 so wrapping it in this way causes a skip
Sam Saffron
@Jared, you mean SaveRest(this IEnumerator<T> enumerable) no... thats one dangerous function, as a side effect it enumerates AND it returns a list.... I ended up coding around this see: http://stackoverflow.com/questions/1018407/what-is-the-most-elegant-way-to-get-a-set-of-items-by-index-from-a-collection/1025137#1025137 but the problem is I lost elegance there
Sam Saffron
@Sam, What would you return besides an IList<T>? I chose a List<T> over an iterator because it removes the issue of dealing with IEnumerater<T> being disposable. Theres' no dispose question if you create the List<T> immediately. If you choose the iterator path, you must deal with lifetime of the IEnumerator<T>
JaredPar
I don't think I have any good solution. I guess the general guildeline of always avoiding any method that takes in a Enumerator as a param should followed.
Sam Saffron
+4  A: 

The easiest way of converting I can think of is via the yield statement

public static IEnumerable<T> ToIEnumerable<T>(this IEnumerator<T> enumerator) {
  while ( enumerator.MoveNext() ) {
    yield return enumerator.Current;
  }
  yield break;
}

compared to the list version this has the advantage of not enumerating the entire list before returning an IEnumerable. using the yield statement you'd only iterate over the items you need, whereas using the list version, you'd first iterate over all items in the list and then all the items you need.

for a little more fun you could change it to

public static IEnumerable<K> Select<K,T>(this IEnumerator<T> enumerator, 
                                         Func<K,T> selector) {
      while ( e.MoveNext() ) {
        yield return selector(e.Current);
      }
      yield break;
    }

you'd then be able to use linq on your enumerator like:

IEnumerator<T> enumerator;
var someList = from item in enumerator
               select new classThatTakesTInConstructor(item);
Rune FS
-1: This doesn't cater for the fact that an IEnumerable should be able to be iterated over multiple times; here it will only be able to be done once as the source IEnumerator will have been used up. You need to cache the items from the IEnumerator for the second time round.
Greg Beech
Well you are stating a requirement not stated (at least explicitly) in the question. If multiple iterations is not required caching will be a performance hit and Sam states that performance is very much a concern hence no caching included
Rune FS