Skip to content

Instantly share code, notes, and snippets.

@hazzik
Last active January 7, 2016 07:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hazzik/b04048732a178e02ff24 to your computer and use it in GitHub Desktop.
Save hazzik/b04048732a178e02ff24 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using NUnit.Framework;
public class CachingEnumerable<T> : IEnumerable<T>, IDisposable
{
List<T> _cache;
IEnumerator<T> _enumerator;
public CachingEnumerable(IEnumerable<T> enumerable)
: this(enumerable.GetEnumerator())
{
}
public CachingEnumerable(IEnumerator<T> enumerator)
{
_enumerator = enumerator;
}
public IEnumerator<T> GetEnumerator()
{
var index = 0;
while (_cache != null && index < _cache.Count)
{
yield return _cache[index];
index = index + 1;
}
while (_enumerator != null && _enumerator.MoveNext())
{
var current = _enumerator.Current;
if (_cache == null)
{
_cache = new List<T>();
}
_cache.Add(current);
yield return current;
index = index + 1;
}
if (_enumerator != null)
{
_enumerator.Dispose();
_enumerator = null;
}
while (_cache != null && index < _cache.Count)
{
yield return _cache[index];
index = index + 1;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
if (_enumerator != null)
{
_enumerator.Dispose();
_enumerator = null;
}
}
}
public static class CachingEnumerable
{
public static CachingEnumerable<T> ToCachingEnumerable<T>(this IEnumerable<T> enumerable)
{
return new CachingEnumerable<T>(enumerable);
}
}
[TestFixture]
public class CachedEnumerableTest
{
int _count;
int IncrementCount(int value)
{
_count++;
return value;
}
[Test]
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
[SuppressMessage("ReSharper", "ReturnValueOfPureMethodIsNotUsed")]
public void EntireListIsEnumeratedForOriginalListOrArray()
{
_count = 0;
Enumerable.Range(1, 40).Select(IncrementCount).ToList();
Assert.AreEqual(40, _count);
_count = 0;
Enumerable.Range(1, 40).Select(IncrementCount).ToArray();
Assert.AreEqual(40, _count);
}
[Test]
[SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")]
public void MatrixEnumerationIteratesAsExpectedWhileStillKeepingEnumeratedValuesCached()
{
_count = 0;
var cachedEnumerable = Enumerable.Range(1, 5).Select(IncrementCount).ToCachingEnumerable();
var matrixCount = 0;
foreach (var x in cachedEnumerable)
{
foreach (var y in cachedEnumerable)
{
foreach (var z in cachedEnumerable)
{
matrixCount++;
}
}
}
Assert.AreEqual(5, _count);
Assert.AreEqual(125, matrixCount);
}
[Test]
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
[SuppressMessage("ReSharper", "ReturnValueOfPureMethodIsNotUsed")]
public void MultipleEnumerationAreNotCachedForOriginalIEnumerable()
{
_count = 0;
var enumerable = Enumerable.Range(1, 40).Select(IncrementCount);
enumerable.Take(3).ToArray();
enumerable.Take(10).ToArray();
enumerable.Take(4).ToArray();
Assert.AreEqual(17, _count);
}
[Test]
[SuppressMessage("ReSharper", "ReturnValueOfPureMethodIsNotUsed")]
public void MultipleEnumerationsAreCached()
{
_count = 0;
var cachedEnumerable = Enumerable.Range(1, 40).Select(IncrementCount).ToCachingEnumerable();
cachedEnumerable.Take(3).ToArray();
cachedEnumerable.Take(10).ToArray();
cachedEnumerable.Take(4).ToArray();
Assert.AreEqual(10, _count);
}
[Test]
public void OrderingCachedEnumerableWorksAsExpectedWhileStillKeepingEnumeratedValuesCached()
{
_count = 0;
var cachedEnumerable = Enumerable.Range(1, 5).Select(IncrementCount).ToCachingEnumerable();
var orderedEnumerated = cachedEnumerable.OrderBy(x => x);
var orderedEnumeratedArray = orderedEnumerated.ToArray(); // Enumerated first time in ascending order.
Assert.AreEqual(5, _count);
for (var i = 0; i < orderedEnumeratedArray.Length; i++)
{
Assert.AreEqual(i + 1, orderedEnumeratedArray[i]);
}
var reorderedEnumeratedArray = orderedEnumerated.OrderByDescending(x => x).ToArray(); // Enumerated second time in descending order.
Assert.AreEqual(5, _count);
for (var i = 0; i < reorderedEnumeratedArray.Length; i++)
{
Assert.AreEqual(5 - i, reorderedEnumeratedArray[i]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment