Last active
March 14, 2017 04:11
-
-
Save pmunin/b1059488d17c52da5a732a100ba6e09f to your computer and use it in GitHub Desktop.
Caching enumerator, making sure source enumerator will be only iterated once
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //latest version is here: https://gist.github.com/b1059488d17c52da5a732a100ba6e09f.git | |
| using System; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| namespace CachingEnumeratorUtils | |
| { | |
| /// <summary> | |
| /// Enumerable doing lazy caching of enumerator, to avoid generating items more than one time | |
| /// </summary> | |
| /// <typeparam name="T"></typeparam> | |
| public class CachingEnumerator<T> : IEnumerable<T>, IDisposable | |
| { | |
| public CachingEnumerator(IEnumerable<T> source) | |
| { | |
| this.sourceEnumerator = source.GetEnumerator(); | |
| } | |
| public CachingEnumerator(IEnumerator<T> source) | |
| { | |
| this.sourceEnumerator = source; | |
| } | |
| /// <summary> | |
| /// Items that were already generated by source enumerator | |
| /// </summary> | |
| public IEnumerable<T> CachedItems { get { return cachedItems; } } | |
| /// <summary> | |
| /// Amount of items that were cached already | |
| /// </summary> | |
| public int CachedItemsCount { get { return cachedItems.Count; } } | |
| /// <summary> | |
| /// Indicates if all items cached and source is complete | |
| /// </summary> | |
| public bool IsCachedAll { get { return sourceEnumeratorFinished; } } | |
| //public IEnumerable<T> Source { get; private set; } | |
| IEnumerator<T> sourceEnumerator; | |
| LinkedList<T> cachedItems = new LinkedList<T>(); | |
| bool sourceEnumeratorFinished = false; | |
| bool sourceEnumeratorStarted = false; | |
| LinkedListNode<T> CacheNext() | |
| { | |
| if (sourceEnumeratorFinished) return null; | |
| sourceEnumeratorStarted = true; | |
| sourceEnumeratorFinished = !sourceEnumerator.MoveNext(); | |
| if (sourceEnumeratorFinished) | |
| { | |
| DisposeSource(); | |
| return null; | |
| } | |
| var node = cachedItems.AddLast(sourceEnumerator.Current); | |
| return node; | |
| } | |
| IEnumerable<T> GetItems() | |
| { | |
| if (!sourceEnumeratorStarted) CacheNext(); | |
| var current = cachedItems.First; | |
| while (current != null) | |
| { | |
| yield return current.Value; | |
| current = current.Next ?? CacheNext(); | |
| } | |
| } | |
| public IEnumerator<T> GetEnumerator() | |
| { | |
| return GetItems().GetEnumerator(); | |
| } | |
| IEnumerator IEnumerable.GetEnumerator() | |
| { | |
| return GetEnumerator(); | |
| } | |
| bool sourceDisposed = false; | |
| void DisposeSource() | |
| { | |
| if (sourceDisposed) return; | |
| sourceEnumerator.Dispose(); | |
| sourceDisposed = true; | |
| } | |
| public void Dispose() | |
| { | |
| } | |
| } | |
| /// <summary> | |
| /// Extensions that help to use CachingEnumerator | |
| /// </summary> | |
| public static class CachingEnumeratorExtensions | |
| { | |
| /// <summary> | |
| /// Make sure enumerator will be only iterated once and items generated will be cached | |
| /// </summary> | |
| /// <typeparam name="T"></typeparam> | |
| /// <param name="source"></param> | |
| /// <returns></returns> | |
| public static CachingEnumerator<T> AsCaching<T>(this IEnumerable<T> source) | |
| { | |
| return new CachingEnumerator<T>(source); | |
| } | |
| /// <summary> | |
| /// Make sure enumerator will be only iterated once and items generated will be cached | |
| /// </summary> | |
| /// <typeparam name="T"></typeparam> | |
| /// <param name="source"></param> | |
| /// <returns></returns> | |
| public static CachingEnumerator<T> AsCaching<T>(this IEnumerator<T> source) | |
| { | |
| return new CachingEnumerator<T>(source); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment