Wilsonhut
Deal with it or don't
Obvious Extension Methods: IEnumerable Cache
March 15, 2012
Posted by on So you don’t want to do a ToList(), but you don’t want to enumerate your IEnumerable<T> more than once? How about an extension method called “Cache” that Caches the output as you go.
- public static class Extensions
- {
- public static CachedEnumerable<T> Cache<T>(this IEnumerable<T> enumerable)
- {
- return new CachedEnumerable<T>(enumerable);
- }
- }
- public class CachedEnumerable<T> : IEnumerable<T>
- {
- IEnumerator<T> _originalEnumerator;
- readonly IEnumerable<T> _originalEnumerable;
- readonly List<T> _cache = new List<T>();
- public CachedEnumerable(IEnumerable<T> enumerable)
- {
- _originalEnumerable = enumerable;
- }
- public IEnumerator<T> GetEnumerator()
- {
- foreach (var t in _cache)
- {
- yield return t;
- }
- if (_originalEnumerator == null)
- {
- _originalEnumerator = _originalEnumerable.GetEnumerator();
- }
- while (_originalEnumerator.MoveNext())
- {
- _cache.Add(_originalEnumerator.Current);
- yield return _originalEnumerator.Current;
- }
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
That’s it. If you want to see it in action, open LINQPad, select Language: “C# Program”, delete everything, paste in the above, then finally, paste in the below:
void Main() { var x = GetNumbers().Cache(); "TAKE 2.".Dump(); x.Take(2).Dump("TWO:"); "Get them all.".Dump(); x.Dump("ALL:"); "Get them all again.".Dump(); x.Dump("ALL:"); } public IEnumerable<T> GetNumbers() { yield return 1.Dump("Numbers is hard"); Thread.Sleep(500); yield return 2.Dump("Numbers is hard"); Thread.Sleep(500); yield return 3.Dump("Numbers is hard"); Thread.Sleep(500); yield return 4.Dump("Numbers is hard"); Thread.Sleep(500); yield return 5.Dump("Numbers is hard"); Thread.Sleep(500); yield return 6.Dump("Numbers is hard"); }
There.