有没有一种内置的方式将IEnumerator转换为IEnumerable

有没有一种内置的方式将IEnumerator<T>转换为IEnumerable<T>

你可以使用下面的工作。

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

这会让你陷入麻烦,尽pipe当人们期望连续的调用GetEnumerator返回不同的枚举数与同一个。 但是,如果一次只能在非常有限的情况下使用,这可能会阻止您。

我build议你尽量不要这样做,因为我想最终会回来困扰你。

按照乔纳森的说法,一个更安全的select是。 您可以使用枚举器并创build其余项目的List<T>

 public static List<T> SaveRest<T>(this IEnumerator<T> e) { var list = new List<T>(); while ( e.MoveNext() ) { list.Add(e.Current); } return list; } 

我能想到的最简单的转换方式是通过yield语句

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

与列表版本相比,这具有在返回IEnumerable之前不枚举整个列表的优点。 使用yield语句只能遍历所需的项目,而使用列表版本时,首先遍历列表中的所有项目,然后遍历所需的所有项目。

多一点乐趣,你可以改变它

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

那么你可以在你的枚举器上使用linq:

 IEnumerator<T> enumerator; var someList = from item in enumerator select new classThatTakesTInConstructor(item); 

EnumeratorEnumerable<T>

IEnumerator<T>IEnumerable<T>线程安全,可重置的适配器

我使用像C ++ forward_iterator概念中的Enumerator参数。

我同意这可能会导致混淆,因为太多的人确实会认为统计员是/可以/可枚举的,但他们不是。

然而,IEnumerator包含了Reset方法的事实导致了这种混淆。 这是我最正确的实现的想法。 它利用IEnumerator.Reset()的实现

Enumerable和Enumerator之间的主要区别在于,Enumerable可能能够同时创build多个Enumerator。 这个实现将大量的工作放在确保EnumeratorEnumerable<T>types永远不会发生的事情上。 有两个EnumeratorEnumerableMode

  • Blocking (意味着第二个调用者将等待直到第一个枚举完成)
  • NonBlocking (意味着枚举器的第二个(并发)请求只是抛出一个exception)

1:74行是实现的,79行是testing代码:)

注2:我没有提到SO方便的任何unit testing框架

 using System; using System.Diagnostics; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Threading; namespace EnumeratorTests { public enum EnumeratorEnumerableMode { NonBlocking, Blocking, } public sealed class EnumeratorEnumerable<T> : IEnumerable<T> { #region LockingEnumWrapper public sealed class LockingEnumWrapper : IEnumerator<T> { private static readonly HashSet<IEnumerator<T>> BusyTable = new HashSet<IEnumerator<T>>(); private readonly IEnumerator<T> _wrap; internal LockingEnumWrapper(IEnumerator<T> wrap, EnumeratorEnumerableMode allowBlocking) { _wrap = wrap; if (allowBlocking == EnumeratorEnumerableMode.Blocking) Monitor.Enter(_wrap); else if (!Monitor.TryEnter(_wrap)) throw new InvalidOperationException("Thread conflict accessing busy Enumerator") {Source = "LockingEnumWrapper"}; lock (BusyTable) { if (BusyTable.Contains(_wrap)) throw new LockRecursionException("Self lock (deadlock) conflict accessing busy Enumerator") { Source = "LockingEnumWrapper" }; BusyTable.Add(_wrap); } // always implicit Reset _wrap.Reset(); } #region Implementation of IDisposable and IEnumerator public void Dispose() { lock (BusyTable) BusyTable.Remove(_wrap); Monitor.Exit(_wrap); } public bool MoveNext() { return _wrap.MoveNext(); } public void Reset() { _wrap.Reset(); } public T Current { get { return _wrap.Current; } } object IEnumerator.Current { get { return Current; } } #endregion } #endregion private readonly IEnumerator<T> _enumerator; private readonly EnumeratorEnumerableMode _allowBlocking; public EnumeratorEnumerable(IEnumerator<T> e, EnumeratorEnumerableMode allowBlocking) { _enumerator = e; _allowBlocking = allowBlocking; } private LockRecursionPolicy a; public IEnumerator<T> GetEnumerator() { return new LockingEnumWrapper(_enumerator, _allowBlocking); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class TestClass { private static readonly string World = "hello world\n"; public static void Main(string[] args) { var master = World.GetEnumerator(); var nonblocking = new EnumeratorEnumerable<char>(master, EnumeratorEnumerableMode.NonBlocking); var blocking = new EnumeratorEnumerable<char>(master, EnumeratorEnumerableMode.Blocking); foreach (var c in nonblocking) Console.Write(c); // OK (implicit Reset()) foreach (var c in blocking) Console.Write(c); // OK (implicit Reset()) foreach (var c in nonblocking) Console.Write(c); // OK (implicit Reset()) foreach (var c in blocking) Console.Write(c); // OK (implicit Reset()) try { var willRaiseException = from c1 in nonblocking from c2 in nonblocking select new {c1, c2}; Console.WriteLine("Cartesian product: {0}", willRaiseException.Count()); // RAISE } catch (Exception e) { Console.WriteLine(e); } foreach (var c in nonblocking) Console.Write(c); // OK (implicit Reset()) foreach (var c in blocking) Console.Write(c); // OK (implicit Reset()) try { var willSelfLock = from c1 in blocking from c2 in blocking select new { c1, c2 }; Console.WriteLine("Cartesian product: {0}", willSelfLock.Count()); // LOCK } catch (Exception e) { Console.WriteLine(e); } // should not externally throw (exceptions on other threads reported to console) if (ThreadConflictCombinations(blocking, nonblocking)) throw new InvalidOperationException("Should have thrown an exception on background thread"); if (ThreadConflictCombinations(nonblocking, nonblocking)) throw new InvalidOperationException("Should have thrown an exception on background thread"); if (ThreadConflictCombinations(nonblocking, blocking)) Console.WriteLine("Background thread timed out"); if (ThreadConflictCombinations(blocking, blocking)) Console.WriteLine("Background thread timed out"); Debug.Assert(true); // Must be reached } private static bool ThreadConflictCombinations(IEnumerable<char> main, IEnumerable<char> other) { try { using (main.GetEnumerator()) { var bg = new Thread(o => { try { other.GetEnumerator(); } catch (Exception e) { Report(e); } }) { Name = "background" }; bg.Start(); bool timedOut = !bg.Join(1000); // observe the thread waiting a full second for a lock (or throw the exception for nonblocking) if (timedOut) bg.Abort(); return timedOut; } } catch { throw new InvalidProgramException("Cannot be reached"); } } static private readonly object ConsoleSynch = new Object(); private static void Report(Exception e) { lock (ConsoleSynch) Console.WriteLine("Thread:{0}\tException:{1}", Thread.CurrentThread.Name, e); } } } 

注3:我认为执行线程locking(尤其是在BusyTable周围)是相当难看的; 不过,我不想诉诸ReaderWriterLock(LockRecursionPolicy.NoRecursion) ,并不想假设.NET 4.0 SpinLock

不,IEnumerator <>和IEnumerable <>完全是不同的野兽。

正如Jason Watts所说 – 不,不是直接的。

如果你真的想,你可以通过IEnumerator <T>循环,把项目放入一个List <T>,然后返回,但是我猜这不是你想要做的。

你不能走这个方向(IEnumerator <T> IEnumerable <T>)的基本原因是IEnumerable <T>表示一个可以枚举的集合,但是IEnumerator <T>是对一组项目的特定列举 – 你不能把特定的实例变成创build它的东西。

 static class Helper { public static List<T> SaveRest<T>(this IEnumerator<T> enumerator) { var list = new List<T>(); while (enumerator.MoveNext()) { list.Add(enumerator.Current); } return list; } public static ArrayList SaveRest(this IEnumerator enumerator) { var list = new ArrayList(); while (enumerator.MoveNext()) { list.Add(enumerator.Current); } return list; } } 

这是我写的一个变体…具体是有点不同。 我想在IEnumerable<T>上做一个MoveNext() ,检查结果,然后把所有东西都放在一个新的IEnumerator<T> ,这个“完整的”(甚至包含IEnumerable<T>已经提取)

 // Simple IEnumerable<T> that "uses" an IEnumerator<T> that has // already received a MoveNext(). "eats" the first MoveNext() // received, then continues normally. For shortness, both IEnumerable<T> // and IEnumerator<T> are implemented by the same class. Note that if a // second call to GetEnumerator() is done, the "real" IEnumerator<T> will // be returned, not this proxy implementation. public class EnumerableFromStartedEnumerator<T> : IEnumerable<T>, IEnumerator<T> { public readonly IEnumerator<T> Enumerator; public readonly IEnumerable<T> Enumerable; // Received by creator. Return value of MoveNext() done by caller protected bool FirstMoveNextSuccessful { get; set; } // The Enumerator can be "used" only once, then a new enumerator // can be requested by Enumerable.GetEnumerator() // (default = false) protected bool Used { get; set; } // The first MoveNext() has been already done (default = false) protected bool DoneMoveNext { get; set; } public EnumerableFromStartedEnumerator(IEnumerator<T> enumerator, bool firstMoveNextSuccessful, IEnumerable<T> enumerable) { Enumerator = enumerator; FirstMoveNextSuccessful = firstMoveNextSuccessful; Enumerable = enumerable; } public IEnumerator<T> GetEnumerator() { if (Used) { return Enumerable.GetEnumerator(); } Used = true; return this; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public T Current { get { // There are various school of though on what should // happens if called before the first MoveNext() or // after a MoveNext() returns false. We follow the // "return default(TInner)" school of thought for the // before first MoveNext() and the "whatever the // Enumerator wants" for the after a MoveNext() returns // false if (!DoneMoveNext) { return default(T); } return Enumerator.Current; } } public void Dispose() { Enumerator.Dispose(); } object IEnumerator.Current { get { return Current; } } public bool MoveNext() { if (!DoneMoveNext) { DoneMoveNext = true; return FirstMoveNextSuccessful; } return Enumerator.MoveNext(); } public void Reset() { // This will 99% throw :-) Not our problem. Enumerator.Reset(); // So it is improbable we will arrive here DoneMoveNext = true; } } 

使用:

 var enumerable = someCollection<T>; var enumerator = enumerable.GetEnumerator(); bool res = enumerator.MoveNext(); // do whatever you want with res/enumerator.Current var enumerable2 = new EnumerableFromStartedEnumerator<T>(enumerator, res, enumerable); 

现在,将被请求enumerable2的第一个GetEnumerator()将通过枚举器enumerator给出。 从第二个开始, enumerable.GetEnumerator()将被使用。