Некоторое время назад у меня была похожая проблема ... Тогда я создал это:
// 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;
}
}
и это
public static class EnumerableExtensions
{
public static bool Any<TSource>(this IEnumerable<TSource> source, out IEnumerable<TSource> newSource)
{
var coll = source as ICollection<TSource>;
if (coll != null)
{
newSource = source;
return coll.Count != 0;
}
var coll2 = source as ICollection;
if (coll2 != null)
{
newSource = source;
return coll2.Count != 0;
}
var enu = source.GetEnumerator();
bool success = enu.MoveNext();
newSource = new EnumerableFromStartedEnumerator<TSource>(enu, success, source);
return success;
}
}
, которые вы используете следующим образом:
var enu = new int[] { 1, 2, 3, 4, 5 }.Where(x => x > 3);
IEnumerable<int> enu2;
Console.WriteLine(enu.Any(out enu2));
foreach (var num in enu2)
{
Console.WriteLine(num);
}
Основная идея состоит в том, что вы начинаете перечислять IEnumerable<>
и перечисляете только первый элемент.Теперь у вас есть частично потребленный IEnumerator<T>
.Затем вы «упаковываете» этот частично потребленный IEnumerator<T>
в новый IEnumerable<T>
«правильно» собранный (пример enu2
) ... Когда этот IEnumerable<T>
будет использован в следующий раз, он вернет частично израсходованный IEnumerator<T>
с некоторыми уловками, чтобы казалось, что это не было на самом деле потреблено.