当你可以在lambda中使用await时,为什么不能在lambda中使用yield?

根据Eric Lippert的说法,匿名迭代器没有添加到该语言中,因为实现它会过于复杂。

这对我来说没问题,在他们继续实施匿名异步方法之前,它并没有打扰我。 编译器必须为异步方法做同样的事情,因为它必须用于迭代器(将它们转换为状态机),所以我很困惑为什么匿名迭代器也不允许,当匿名异步方法时。

有人可以对此有所了解吗?

根据Eric Lippert的说法,匿名迭代器没有添加到该语言中,因为实现它会过于复杂。

这不正是我打算传达的。 相关成本是实现成本,是的,但它是现有编译器中的实现成本,其在架构上未设置以实现该复杂function

编译器必须为异步方法做同样的事情,因为它必须用于迭代器(将它们转换为状态机),所以我很困惑为什么匿名迭代器也不允许,当匿名异步方法时。

简短的历史是相关的。 C#首先在C#2.0中使用了匿名方法和迭代器块。 当我在C#3.0中添加lambdas时,重构所有现有的匿名方法代码是一个主要的代价,这样它就可以处理lambdas的所有新function。 这使得修改变得更加复杂和昂贵。 制造迭代器阻挡lambdas的成本太高,无法产生的好处; 它将占总成本的很大一部分。 我们负担不起。 如果你在Developer Division的工作时间表中加入了每个团队,那么具有“最长极点”的团队就是C#3.0编译团队,而我在语义分析器上的工作是IIRC,它是编译器团队中最长的极点。 每天我们都可能会放弃C#3.0,那将是Visual Studio可能会滑落的一天。 因此,任何没有使LINQ更好的东西都被削减了,包括迭代器lambda。

在C#4中,迭代器lambda是许多被考虑的特征之一。 我们有一个潜在的良好function列表,比你的arm更长,我们可以负担不到十分之一。

在C#5中,团队添加了异步方法。 设计和实现团队长期尝试提出一个底层抽象,它对迭代器块和等待重写都很常见; 你注意到它们显然是相似的。 但最终,找到一般解决方案的成本并不能为此付出代价。 普遍性是非常昂贵的,并且发现一般性,即设计只统一件事,如果不便宜则是愚蠢的。

因此决定将await rewriter作为自己的东西来实现。 鉴于该团队将承担这笔巨额费用,并且鉴于异步方法的原始转换无论如何都将变为lambdaforms,因此决定投资于完整function:包含lambdas的异步方法,async lambdas包含lambda,整个交易。 该function的成本只是整个function成本的一小部分,这非常昂贵。

再说一次,我们遇到长杆问题。 可以避免在lambda引擎上进行任何可能会破坏稳定await工作,包括尝试使它们与迭代器块一起工作。

现在比较Visual Basic。 VB很长一段时间都没有迭代器块。 添加它们后,没有现成的基础设施可以继续工作! 整个过程可以从头开始构建,以处理包含lambda和包含迭代器块的lambda的迭代器块,这样就完成了。

C#编译器已通过Roslyn项目进行了彻底的重新架构和重写。 我希望这将降低在假设的未来版本的C#中实现迭代器块lambdas的成本。 我们会看到的!

匿名迭代器块,虽然不错,但没有特别引人注目的好处。 迭代器块被重构为自己的方法并不是一个巨大的威慑。

async匿名方法更具概念意义,不要像匿名迭代器块那样保证重构自己的方法,并且具有更具吸引力的最终用户权益。

简而言之,与迭代器块不同,实现的好处是值得的。 成本可能相当可比。

看看这段代码(它不起作用,只是一个例子):

 Task> resultTask = new Task>(() => { for (int i = 0; i < 10; ++i) { yield return i; } }); 

难道你不觉得它是某种非结构化的吗?

假设整个范围的lambda用法,那将是非常困难并且不值得正确处理yield “懒惰” 。

但是,有很多方法可以从并行任务中yield回报 。

但是让我们来看看下面的事情。 使用yield return定义方法:

 static IEnumerable GetIntegers() { for (int i = 0; i < 10; ++i) { yield return i; } } 

把它放在lambda中起作用:

 Task> resultTask = new Task>(() => { return GetIntegers(); }); 

这段代码的行为方式是什么? 是否会失去实际的yield优势?