埃里克·利珀特(Eric Lippert)就迭代器块的局限性(以及影响这些选择的设计决策)撰写了一系列精彩的文章
特别是,迭代器块是通过一些复杂的编译器代码转换实现的。这些转换将影响发生在匿名函数或lambda内部的转换,因此在某些情况下,它们都将试图将代码“转换”为与其他结构不兼容的其他结构。
结果,它们被禁止交互。
这里很好地解决了迭代器块如何在后台工作的问题。
作为不兼容的简单示例:
public IList<T> GreaterThan<T>(T t)
{
IList<T> list = GetList<T>();
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
return items.ToList();
}
编译器同时希望将其转换为以下内容:
private class Magic
{
private T t;
private IList<T> list;
private Magic(List<T> list, T t) { this.list = list; this.t = t;}
public IEnumerable<T> DoIt()
{
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
}
}
public IList<T> GreaterThan<T>(T t)
{
var magic = new Magic(GetList<T>(), t)
var items = magic.DoIt();
return items.ToList();
}
同时,迭代器方面正在尝试做一些小的状态机。某些简单的示例可能需要进行大量的健全性检查(首先处理(可能任意地)嵌套的闭包),然后查看生成的最底层的类是否可以转换为迭代器状态机。
但是,这将是
- 很多工作。
- 至少没有迭代器块方面能够阻止闭包方面应用某些转换以提高效率(例如将局部变量提升为实例变量而不是完全成熟的闭包类),不可能在所有情况下都有效。
- 如果在不可能或很难实施的情况下,甚至有很小的重叠机会,那么所产生的支持问题的数量可能会很高,因为微妙的突破性变化会丢失给许多用户。
- 它可以很容易地解决。
在您的示例中,如下所示:
public IList<T> Find<T>(Expression<Func<T, bool>> expression)
where T : class, new()
{
return FindInner(expression).ToList();
}
private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression)
where T : class, new()
{
IList<T> list = GetList<T>();
var fun = expression.Compile();
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
async
允许匿名lambdaawait
进入内部,我很想知道为什么他们仍然没有使用yield
内部实现匿名迭代器。或多或少,它是相同的状态机生成器。