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; // 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;
}