Eric Lippert 在iterator blocks 上撰写了一系列关于限制(以及影响这些选择的设计决策)的优秀文章
特别是迭代器块是由一些复杂的编译器代码转换实现的。这些转换会影响匿名函数或 lambda 内部发生的转换,因此在某些情况下,它们都会尝试将代码“转换”为与其他不兼容的其他构造。
因此他们被禁止互动。
迭代器块如何在后台工作得到很好的处理here。
作为一个不兼容的简单示例:
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; // This is not allowed by C#
}
return items.ToList();
}
编译器同时希望将其转换为:
// inner class
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;
}