我知道很多关于Linq和它的内部运作的文章。在Jon Skeets EduLinq的启发下,我想揭开Linq运营商背后的神秘面纱。所以我试图做的是实现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。但最后,这只是一种常规方法,所以我想知道那里发生了什么?
答案 0 :(得分:5)
每次在结果上调用MoveNext()
时,它都会:
MoveNext()
,这将... MoveNext()
,这将... MoveNext()
,这将返回一个数字。MoveNext()
会调用投影x => x.ToString()
,以便它有一个合适的Current
成员MoveNext()
会在x => x + "test"
的结果上调用预测X.Current
,并将结果存储在Y.Current
所以这里没有什么特别神奇的东西 - 你只是像往常一样堆叠了电话。调试体验将显示从EnumeratorWrapper.MoveNext
到另一个Main
的呼叫;当您单步执行{{1}}方法中声明的投影时,唯一奇怪的跳跃就是。
如果这不能解释令您感到困惑的内容,请详细了解完全您不了解流程的详细信息,我会看到如何提供帮助。 (很高兴看到你想要更多地了解所有这些东西是如何工作的。很高兴看到一个相似的精神!)