有人可以揭开yield关键字的神秘面纱吗?
我在 Stack Overflow 和博客上看到过很多次使用 Yield 关键字。我不使用 LINQ。有人可以解释一下yield关键字吗?
我知道也存在类似的问题。 但没有人真正用简单的语言解释它的用途。
I have seen the yield keyword being used quite a lot on Stack Overflow and blogs. I don't use LINQ. Can someone explain the yield keyword?
I know that similar questions exist.
But none really explain what is its use in plain simple language.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
为了揭开神秘面纱,我将避免谈论迭代器,因为它们本身可能就是谜团的一部分。
Yield return 和 Yield Break 语句最常用于提供集合的“延迟评估”。
这意味着当你获取使用yield return的方法的值时,你试图获取的东西的集合还不存在(它本质上是空的)。当您循环遍历它们时(使用 foreach),它将执行当时的方法并获取枚举中的下一个元素。
某些属性和方法将导致整个枚举立即被计算(例如“Count”)。
下面是返回集合和返回yield之间差异的一个简单示例:
如果您需要在源数据具有值之前获取对枚举的引用,也可以使用此示例。例如,如果名称收集一开始就不完整:
In an effort to demystify I'll avoid talking about iterators, since they could be part of the mystery themselves.
the yield return and yield break statements are most often used to provide "deferred evaluation" of the collection.
What this means is that when you get the value of a method that uses yield return, the collection of things you are trying to get don't exist together yet (it's essentially empty). As you loop through them (using foreach) it will execute the method at that time and get the next element in the enumeration.
Certain properties and methods will cause the entire enumeration to be evaluated at once (such as "Count").
Here's a quick example of the difference between returning a collection and returning yield:
This can also be used if you need to get a reference to the Enumeration before the source data has values. For example if the names collection wasn't complete to start with:
yield
关键字是编写IEnumerator
的便捷方法。例如:由 C# 编译器转换为类似于:
The
yield
keyword is a convenient way to write anIEnumerator
. For example:is transformed by the C# compiler to something similiar to:
请查看 MSDN 文档和示例。它本质上是在 C# 中创建迭代器的一种简单方法。
Take a look at the MSDN documentation and the example. It is essentially an easy way to create an iterator in C#.
Eric White 的函数式编程系列非常值得完整阅读,但是 Yield 条目 非常清楚正如我所看到的解释。
Eric White's series on functional programming it well worth the read in it's entirety, but the entry on Yield is as clear an explanation as I've seen.
yield
与 LINQ 没有直接关系,而是迭代器块。链接的 MSDN 文章 提供了有关此语言功能的详细信息。特别请参阅使用迭代器部分。有关迭代器块的详细信息,请参阅 Eric Lippert 最近的博客帖子 关于功能。有关一般概念,请参阅维基百科有关迭代器的文章。yield
is not directly related to LINQ, but rather to iterator blocks. The linked MSDN article gives great detail on this language feature. See especially the Using Iterators section. For deep details of iterator blocks, see Eric Lippert's recent blog posts on the feature. For the general concept, see the Wikipedia article on iterators.我想出这个来克服 .NET 必须手动深层复制列表的缺点。
我使用这个:
在另一个地方:
我试图想出一个oneliner来做到这一点,但这是不可能的,因为yield在匿名方法块内不起作用。
编辑:
更好的是,使用通用列表克隆器:
I came up with this to overcome a .NET shortcoming having to manually deep copy List.
I use this:
And at another place:
I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.
EDIT:
Better still, use a generic List cloner:
让我补充一下这一切。产量不是关键词。
仅当您使用“yield return”时它才会起作用,而不是像普通变量一样工作。
它用于从函数返回迭代器。您可以对此进行进一步搜索。
我建议搜索“返回数组与迭代器”
Let me add to all of this. Yield is not a keyword.
It will only work if you use "yield return" other than that it will work like a normal variable.
It's uses to return iterator from a function. You can search further on that.
I recommend searching for "Returning Array vs Iterator"
到目前为止(我见过的)对此最好的解释是乔恩·斯基特(Jon Skeet)的书 - 并且该章是免费的!第 6 章,深入了解 C#。我可以在这里添加任何未涵盖的内容。
然后买书;你将因此成为一名更好的 C# 程序员。
问:为什么我不在这里写一个更长的答案(从评论中解释);简单的。正如埃里克·利珀特(Eric Lippert)观察到的(此处),
yield
构造(及其背后的魔力)是C# 编译器中最复杂的代码位,试图在这里简短的回复中描述它充其量是天真的。yield
有很多细微差别,在我看来,最好参考预先存在的(且完全合格的)资源。Eric 的博客现在有 7 个讨论
yield
的条目(这只是最近的条目)。我对埃里克怀有极大的敬意,但他的博客可能更适合作为那些熟悉该主题的人的“更多信息”(yield
在本例中),因为它通常描述了许多背景设计考虑因素。最好在合理的基础上进行。(是的,第 6 章确实下载;我已验证......)
By far the best explanation of this (that I've seen) is Jon Skeet's book - and that chapter is free! Chapter 6, C# in Depth. There is nothing I can add here that isn't covered.
Then buy the book; you will be a better C# programmer for it.
Q: Why didn't I write a longer answer here (paraphrased from comments); simple. As Eric Lippert observes (here), the
yield
construct (and the magic that goes behind it) is the single most complex bit of code in the C# compiler, and to try and describe it in a brief reply here is naïve at best. There are so many nuances toyield
that IMO it is better to refer to a pre-existing (and fully qualified) resource.Eric's blog now has 7 entries (and that is just the recent ones) discussing
yield
. I have a vast amount of respect for Eric, but his blog is probably more appropriate as a "further information" for people who are comfortable with the subject (yield
in this case), as it typically describes a lot of the background design considerations. Best done in the context of a reasonable foundation.(and yes, chapter 6 does download; I verified...)
yield
关键字与返回IEnumerable
或IEnumerator
的方法一起使用,它使编译器生成一个实现使用迭代器的必要管道。例如,鉴于上述情况,编译器将生成一个实现
IEnumerator
、IEnumerable
和IDisposable
的类(实际上它也会实现IEnumerable
和IEnumerator
的非通用版本)。这允许您在
foreach
循环中调用方法SequenceOfOneToThree
,如下所示迭代器是一个状态机,因此每次调用
yield
时位置方法中记录了。如果迭代器移动到下一个元素,该方法将在此位置之后立即恢复。因此第一次迭代返回 1 并标记该位置。下一个迭代器在 1 之后立即恢复,从而返回 2 等等。不用说,您可以按照您喜欢的任何方式生成序列,因此您不必像我一样对数字进行硬编码。另外,如果您想中断循环,可以使用
yield break
。The
yield
keyword is used with methods that returnIEnumerable<T>
orIEnumerator<T>
and it makes the compiler generate a class that implements the necessary plumbing for using the iterator. E.g.Given the above the compiler will generate a class that implements
IEnumerator<int>
,IEnumerable<int>
andIDisposable
(actually it will also implement the non-generic versions ofIEnumerable
andIEnumerator
).This allows you to call the method
SequenceOfOneToThree
in aforeach
loop like thisAn iterator is a state machine, so each time
yield
is called the position in the method is recorded. If the iterator is moved to the next element, the method resumes right after this position. So the first iteration returns 1 and marks that position. The next iterator resumes right after one and thus returns 2 and so forth.Needless to say you can generate the sequence in any way you like, so you don't have to hard code the numbers like I did. Also, if you want to break the loop you can use
yield break
.