“yield关键字”在迭代器块之外有用吗?

9

关于yield关键字的文档说明:

yield关键字用于向编译器传达它所在的方法是一个迭代器块。

我遇到了在任何迭代器块之外使用yield关键字的代码。这应该被视为编程错误还是可以接受的?

编辑 抱歉忘记发布我的代码:

int yield = previousVal/actualVal;
return yield; // Should this be allowed!!!???

感谢您的选择。

4
想出“return yield;”这行代码的人是一个史诗级别的邪恶天才。 - Marc Gravell
我们有一些非常不错的代码库!我之所以问这个问题是因为我正在反对这种编码方式… 我想听听社区对此的看法。 - GETah
这并没有本质上的问题 - 除非你可能会争辩说,它让你提出这个问题的事实意味着它有潜力让人感到困惑。使用“商”或“比率”这个词会避免很多头痛 :p - Marc Gravell
我眼前没有编译器,但很惊讶它不需要声明 int @yield。我猜这是因为它本身不是一个上下文关键字。 - John K
@JohnK 正是因为 @yield 是一个上下文关键字,所以你不需要输入它。上下文关键字仅在特定上下文中作为关键字起作用。而像 class 这样的非上下文关键字在每个上下文中始终都是关键字,因此如果要将它们用作标识符,则需要在前面添加 @ 符号。 - phoog
显示剩余3条评论
3个回答

22

在迭代器块之外使用 yield 是可以的 - 这仅表示它不被用作上下文关键字。

例如:

// No idea whether this is financially correct, but imagine it is :)
decimal yield = amountReturned / amountInvested;

在这种情况下,它不是一个上下文关键字(它从来不是一个“完整”的关键字),它只是一个标识符。除非在正常清晰度方面明显是最佳选择,否则我会尽量避免使用它,但有时候可能需要使用。

您只能将其用作yield returnyield break的上下文关键字,这些仅在迭代器块中有效。 (它们是什么将方法转换为迭代器。)

编辑:回答你的“是否应该允许”问题……是的,应该。否则,C# 2发布时使用yield作为标识符的所有现有C# 1代码都将变为无效。它应该小心使用,并且C#团队确保它实际上从未产生歧义,但是它可以有效地使用。

对于许多其他上下文关键字也是如此,“from”、“select”、“where”等。您想要防止那些永远不会成为标识符吗?


是的,这正是我的观点。 - GETah
1
@GETah:嗯,在你提供的上下文中,它并不是一个真正的“关键词”... - Jon Skeet
Jon,我在这里测试我的理解... yield 在像我在我的回答中的 GetNotVeryRandomNumbers 方法中使用时,不是在迭代器块中吗?(我认为 yield 创建了一个,但不必存在于其中)。这似乎与您最后的陈述相矛盾。 - George Duckett
重新阅读,我认为你的意思是yield break必须在迭代器块中。我原以为你的意思是两者都必须在迭代器块中。 - George Duckett
2
在引入yield上下文关键字之前,“return yield”的+1可能已经存在。 - Peter Ritchie
显示剩余2条评论

9

这句话的意思是,如果你在方法中使用yield,那么该方法就变成了一个迭代器块。

此外,由于yield是一个上下文关键字,它可以自由地在其他地方使用,而完整的关键字则不能(例如变量名)。

void AnotherMethod()
{
    foreach(var NotRandomNumber in GetNotVeryRandomNumbers())
    {
        Console.WriteLine("This is not random! {0}", NotRandomNumber);
    }
}

IEnumerable<int> GetNotVeryRandomNumbers()
{
    yield return 1;
    yield return 53;
    yield return 79;
    yield return 1;
    yield return 789;
}

// Perhaps more useful function below: (untested but you get the idea)
IEnumerable<Node> DepthFirstIteration(Node)
{
    foreach(var Child in Node.Children)
    {
        foreach(var FurtherChildren in DepthFirstIteration(Child))
        {
            yield return FurtherChildren;
        }
    }
    yield return Node;
}

参见:这个答案,展示了如何在一个yield之后使用另一个yield。 迭代器块的聪明用法


我可能错了,但我认为这样做忽略了问题的重点。问题不是“迭代器块有用吗?”-而是:在迭代器块之外使用时,“yield”有用吗? - Marc Gravell
GetNotVeryRandomNumbers 方法中,它不是在迭代器块中(它创建了一个),或者我误解了迭代器块? - George Duckett
通过编辑,很明显我确实错过了问题的重点!(哎呀) - George Duckett
@GeorgeDuckett 我认为更准确的说法是编译器将该方法视为迭代器块,而不是调用代码这样做。对于调用代码来说,该方法只是返回IEnumerable或IEnumerator或它们的泛型对应项的方法。调用代码不知道实现细节(无论该方法是作为迭代器块编码还是仅作为返回实现这些接口之一的对象的方法)。换句话说,编译器将迭代器块编译为返回实现适当接口的对象的方法。 - phoog
@GeorgeDuckett 现在如果我能编辑那个评论来修复最后一句话的丑陋就好了。 - phoog

5

yield是一个上下文关键字。当用于yield returnyield break时,它是一个关键字并创建迭代器。在其他地方使用时,它是一个普通的标识符。

yield对于非常规集合非常有用;它也可以用于实现原始协程。


+1 链接到您之前有趣问题的链接 - phoog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接