Lan*_*her 85 c# yield anonymous-methods yield-return
我认为做这样的事情会很好(lambda做一个yield return):
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
IList<T> list = GetList<T>();
var fun = expression.Compile();
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item; // This is not allowed by C#
}
return items.ToList();
}
Run Code Online (Sandbox Code Playgroud)
但是,我发现我不能在匿名方法中使用yield.我想知道为什么.该产量的文档只是说,这是不允许的.
由于不允许,我只创建了List并将项目添加到其中.
Tho*_*que 105
Eric Lippert最近撰写了一系列关于为什么在某些情况下不允许收益的博客文章.
EDIT2:
你可能会在那里找到答案......
EDIT1:在第5部分的评论中解释了这一点,在Eric对Abhijeet Patel评论的回答中:
问:
埃里克,
您是否还可以深入了解为什么在匿名方法或lambda表达式中不允许"yield"
A :
好问题.我很想拥有匿名迭代器块.能够在适当的位置建立一个关闭局部变量的小序列生成器将是非常棒的.之所以不直截了当:好处不会超过成本.在大型方案中,制作顺序生成器的实际功能实际上非常小,而名义方法在大多数情况下都能很好地完成工作.所以好处并不那么引人注目.
成本很高.迭代器重写是编译器中最复杂的转换,而匿名方法重写是第二复杂的.匿名方法可以在其他匿名方法中,匿名方法可以在迭代器块内.因此,我们首先要重写所有匿名方法,以便它们成为闭包类的方法.这是编译器在为方法发出IL之前所做的第二件事.完成该步骤后,迭代器重写器可以假定迭代器块中没有匿名方法; 他们都已经被重写了.因此迭代器重写器可以专注于重写迭代器,而不必担心那里可能存在未实现的匿名方法.
此外,与匿名方法不同,迭代器块永远不会"嵌套".迭代器重写器可以假设所有迭代器块都是"顶级".
如果允许匿名方法包含迭代器块,那么这两个假设都会从窗口中消失.您可以拥有一个迭代器块,其中包含一个匿名方法,该方法包含一个匿名方法,该方法包含一个包含匿名方法的迭代器块,并且... yuck.现在我们必须编写一个可以同时处理嵌套迭代器块和嵌套匿名方法的重写传递,将我们最复杂的两个算法合并为一个更复杂的算法.设计,实施和测试真的很难.我很聪明,我敢肯定.我们这里有一个聪明的团队.但是我们不想承担"很好但没有必要"功能的巨大负担. - 埃里克
Shu*_*oUk 21
Eric Lippert撰写了一系列关于迭代器块的限制(以及影响这些选择的设计决策)的优秀文章
特别是迭代器块是通过一些复杂的编译器代码转换实现的.这些转换会影响匿名函数或lambdas中发生的转换,这样在某些情况下它们都会尝试将代码"转换"为另一个与另一个不兼容的构造.
结果他们被禁止互动.
迭代器块如何在引擎盖下工作在这里处理得很好.
作为不兼容性的简单示例:
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();
}
Run Code Online (Sandbox Code Playgroud)
编译器同时想要将其转换为:
// 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();
}
Run Code Online (Sandbox Code Playgroud)
同时迭代器方面试图做一个小型状态机的工作.某些简单的示例可能需要进行大量的完整性检查(首先处理(可能是任意)嵌套的闭包)然后查看最底层的结果类是否可以转换为迭代器状态机.
不过这将是
在你的例子中如此:
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;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不知道他们为什么不允许这样做,因为当然完全有可能想象这将如何工作。
然而,匿名方法已经是一种“编译器魔术”,因为该方法将被提取到现有类中的方法,甚至是一个全新的类,具体取决于它是否处理局部变量。
此外,迭代器方法 usingyield也是使用编译器魔法实现的。
我的猜测是,这两个中的一个使代码无法被另一个魔法识别,并且决定不花时间为当前版本的 C# 编译器进行这项工作。当然,这可能根本就不是一个有意识的选择,它只是行不通,因为没有人想过要实施它。
对于 100% 准确的问题,我建议您使用Microsoft Connect站点并报告问题,我相信您会得到一些有用的回报。