Die hier gezeigte Klasse nimmt einen Enumerator (IEnumerator<T>) oder eine Auflistung (IEnumerable<T>) entgegen und durchläuft diese immer wieder im Kreis. Würde mein beispielsweise ein Array mit den Werten 1,2,3 übergeben, würde diese Klasse 1,2,3,1,2,3,1,2,3,1,2,3,... usw. zurück geben.
Mit Hilfe der break-Anweisung in einer foreach-Schleife bzw. passenden LINQ-Methoden oder einem manuellen verwenden der IEnumerator Schnittstelle kann man die "Dauerschleife" wieder anhalten.
Diese Klasse auf GitHub
http://bit.ly/1KdwukE
UnitTests zu diese Klasse auf GitHub
http://bit.ly/1WQ99rd
using System;
using System.Collections;
using System.Collections.Generic;
namespace Koopakiller.Linq
{
public static class Extensions
{
/// <summary>
/// Duchläuft eine Auflistung immer wieder im Kreis.
/// </summary>
/// <typeparam name="T">Der Typ der Elemente in der Auflistung.</typeparam>
/// <param name="source">Die zu durchlaufende Auflistung.</param>
/// <param name="useCache">Wenn <c>true</c> dann wird <paramref name="source"/> nur einmal
/// durchlaufen; andernfalls wird mehrfach über die Auflistung iteriert.</param>
public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source, bool useCache = true)
{
if (useCache)
{
source = source.ToList();
}
while (true)
{
// ReSharper disable once PossibleMultipleEnumeration
foreach (var item in source)
{
yield return item;
}
}
// ReSharper disable once FunctionNeverReturns
}
}
public sealed class RingEnumerator<T> : IEnumerable<T>, IEnumerator<T>
{
#region Fields
private readonly IEnumerator<T> _enumerator;
private bool _lastMoveNextWasFalse = true;
private bool _iterationStarted;
private bool _useCache;
private List<T> _cachedList;
private int _currentCachedItemIndex = -1;
private bool _cacheBuildComplete;
#endregion
#region .ctor
public RingEnumerator(IEnumerable<T> source) : this(source?.GetEnumerator()) { }
public RingEnumerator(IEnumerator<T> enumerator)
{
if (enumerator == null)
{
throw new ArgumentNullException(nameof(enumerator), $"{nameof(enumerator)} cannot be null.");
}
this._enumerator = enumerator;
}
#endregion
#region Properties
public bool UseCache
{
get { return this._useCache; }
set
{
if (this._iterationStarted)
{
throw new InvalidOperationException("The enumeration has already started.");
}
this._useCache = value;
}
}
#endregion
#region IEnumerator
object IEnumerator.Current => this.Current;
public T Current
{
get
{
if (this.UseCache && this._cacheBuildComplete)
{
return this._cachedList[this._currentCachedItemIndex];
}
else
{
return this._enumerator.Current;
}
}
}
public void Reset()
{
this._currentCachedItemIndex = -1;
this._cacheBuildComplete = false;
this._iterationStarted = false;
this._enumerator.Reset();
}
public bool MoveNext()
{
if (!this._iterationStarted && this.UseCache)
{
this._cacheBuildComplete = false;
this._cachedList = new List<T>();
}
if (this.UseCache && this._cacheBuildComplete)
{
++this._currentCachedItemIndex;
if (this._currentCachedItemIndex >= this._cachedList.Count)
{
this._currentCachedItemIndex = 0;
}
return true;
}
else
{
this._iterationStarted = true;
if (this._enumerator.MoveNext())
{
this._lastMoveNextWasFalse = false;
if (this.UseCache)
{
this._cachedList.Add(this.Current);
}
return true;
}
if (this.UseCache)
{
this._cacheBuildComplete = true;
this._currentCachedItemIndex = 0;
return this._cachedList.Count > 0;
}
else
{
if (this._lastMoveNextWasFalse)
{
return false;
}
this._lastMoveNextWasFalse = true;
this._enumerator.Reset();
this._enumerator.MoveNext();
return true;
}
}
}
#endregion
#region IDisposable
public void Dispose()
{
this._enumerator.Dispose();
}
#endregion
#region IEnumerable
public IEnumerator<T> GetEnumerator() => this;
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
#endregion
}
}
4 Kommentare zum Snippet