对我来说没关系,在他们继续实施匿名异步方法之前,它并没有打扰我。编译器必须对异步方法执行与迭代器相同的操作(将它们转换为状态机),所以我很困惑为什么匿名迭代器也不允许,当匿名异步方法是。
有人可以对此有所了解吗?
答案 0 :(得分:22)
根据Eric Lippert的说法,匿名迭代器没有被添加到语言中,因为实现它会过于复杂。
这不正是我打算传达的。相关的成本是实现成本,是的,但它是现有编译器中的实现成本,它没有在架构上设置来实现复杂的功能。
编译器必须对异步方法执行与迭代器相同的操作(将它们转换为状态机),所以我很困惑为什么匿名迭代器也不允许,当匿名异步方法是。
简短的历史是相关的。 C#首先在C#2.0中使用了匿名方法和迭代器块。当我在C#3.0中添加lambdas时,重构所有现有的匿名方法代码是一项重大成本,因此它可以处理lambdas的所有新功能。这使得修改变得更加复杂和昂贵。制造迭代器阻挡lambdas的成本太高,无法产生的收益;它将占总成本的很大一部分。 我们负担不起。如果你在开发部门的工作时间表中加入了每个团队,那么“最长极”的团队就是C#3.0编译团队,而我在语义分析器上的工作是IIRC编译器团队中最长的极点。每天我们可能都会放弃C#3.0,这可能是Visual Studio可能会滑落的一天。因此,任何没有使LINQ更好的东西都会被削减,包括迭代器lambda。
在C#4中,迭代器lambdas是许多被考虑的特征之一。我们有一个潜在的良好功能列表,比你的手臂更长,我们可以负担不到十分之一。
在C#5中,团队添加了异步方法。设计和实现团队长期尝试提出一个底层抽象,它对迭代器块和等待重写都很常见;你注意到它们显然是相似的。但最终,找到一般解决方案的成本并不能为此付出代价。普遍性是非常昂贵的,并且发现通用设计只统一两个事物的一般性是愚蠢的,如果它不便宜。
因此决定将await rewriter作为自己的东西来实现。鉴于该团队将承担这笔巨额费用,并且鉴于异步方法的原始转换无论如何都将变为lambda形式,因此决定投资于完整功能:包含lambdas的异步方法,async lambdas包含lambdas,整个交易。该功能的成本只是整个功能成本的一小部分,这非常昂贵。
同样,我们遇到长杆问题。应该避免对lambda引擎进行任何可能导致不稳定await
的工作,包括尝试使它们与迭代器块一起使用。
现在比较Visual Basic。 VB很长一段时间都没有迭代器块。添加它们后,没有现有的基础设施可以继续工作!整个事情可以从头开始构建,以处理包含lambdas和包含迭代器块的lambda的迭代器块,这样就完成了。
C#编译器已经彻底重新架构并通过Roslyn项目重写。我希望这将降低在假设的未来版本的C#中实现迭代器块lambdas的成本。我们会看到!
答案 1 :(得分:5)
匿名迭代器块,虽然不错,但没有特别引人注目的好处。迭代器块被重构为自己的方法并不是一个巨大的威慑力。
async
匿名方法更具概念意义,不保证重构到自己的方法,就像匿名迭代器块一样,并且有一个很多更具吸引力的最终用户益处。
简而言之,与迭代器块不同,实现的好处是值得的。成本可能相当可比。
答案 2 :(得分:1)
看看这段代码(它不起作用,只是一个例子):
Task<IEnumerable<int>> resultTask = new Task<IEnumerable<int>>(() =>
{
for (int i = 0; i < 10; ++i)
{
yield return i;
}
});
你不觉得它是某种非结构化的吗?
假设使用lambda的范围很广,那么正确处理yield
"laziness"会非常困难而且不值得。
然而,有很棒的approaches to yield
return from parallel tasks。
但是让我们来看看下面的事情。使用yield
返回定义方法:
static IEnumerable<int> GetIntegers()
{
for (int i = 0; i < 10; ++i)
{
yield return i;
}
}
将它放在lambda 将工作:
Task<IEnumerable<int>> resultTask = new Task<IEnumerable<int>>(() =>
{
return GetIntegers();
});
此代码的行为方式是什么?是否会失去真正的yield
优势?