Я знаю, что много написано о Linq и его внутренней работе. Вдохновленный Джоном Скитсом EduLinq, я хотел рассказать о том, что происходит за Linq Operators. Поэтому я попытался реализовать метод Linqs Select (), который на первый взгляд звучит довольно скучно. Но на самом деле я пытаюсь реализовать это без использования ключевого слова yield.
Итак, вот что я получил:
class Program
{
static void Main(string[] args)
{
var list = new int[] {1, 2, 3};
var otherList = list.MySelect(x => x.ToString()).MySelect(x => x + "test");
foreach (var item in otherList)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
public static class EnumerableEx
{
public static IEnumerable<R> MySelect<T, R>(this IEnumerable<T> sequence, Func<T, R> apply)
{
return new EnumerableWrapper<R, T>(sequence, apply);
}
}
public class EnumerableWrapper<T, O> : IEnumerable<T>
{
private readonly IEnumerable<O> _sequence;
private readonly Func<O, T> _apply;
public EnumerableWrapper(IEnumerable<O> sequence, Func<O, T> apply)
{
_sequence = sequence;
_apply = apply;
}
public IEnumerator<T> GetEnumerator()
{
return new EnumeratorWrapper<T, O>(_sequence, _apply);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class EnumeratorWrapper<T, O> : IEnumerator<T>
{
private readonly IEnumerator<O> _enumerator;
private readonly Func<O, T> _apply;
public EnumeratorWrapper(IEnumerable<O> sequence, Func<O, T> apply)
{
_enumerator = sequence.GetEnumerator();
_apply = apply;
}
public void Dispose()
{
}
public bool MoveNext()
{
var hasItems = _enumerator.MoveNext();
if (hasItems)
Current = _apply(_enumerator.Current);
return hasItems;
}
public void Reset()
{
_enumerator.Reset();
}
public T Current { get; private set; }
object IEnumerator.Current
{
get { return Current; }
}
}
Кажется, работает. Тем не менее, у меня есть проблемы, чтобы следить за его потоком управления. Как видите, я цепляюсь за прогнозы. Это приводит к тому, что странные вещи (странные для меня!) Происходят в методе MoveNext (). Если вы установите точки останова в каждой строке метода MoveNext (), вы увидите, что поток управления фактически переходит между различными экземплярами и никогда не работает через метод в одном пакете. Он прыгает, как если бы он использовал разные потоки или если мы использовали yield. Но, в конце концов, это просто нормальный метод, поэтому мне интересно, что там происходит?