产生关键词附加值?
仍在尝试找到在实际情况下我会在哪里使用“yield”关键字。
我看到这个主题的主题
但在接受的答案中,他们以此作为示例,其中有人正在迭代 Integers()
public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}
但为什么不直接
list<int>
在这里使用。 看起来更简单..
still trying to find where i would use the "yield" keyword in a real situation.
I see this thread on the subject
What is the yield keyword used for in C#?
but in the accepted answer, they have this as an example where someone is iterating around Integers()
public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}
but why not just use
list<int>
here instead. seems more straightforward..
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
如果您构建并返回一个 List(假设它有 100 万个元素),那么这将占用很大的内存,并且创建它也需要大量工作。
有时调用者可能只想知道第一个元素是什么。 或者他们可能想在获取它们时将它们写入文件,而不是在内存中构建整个列表,然后将其写入文件。
这就是为什么使用收益率更有意义。 它看起来与构建整个列表并返回它没有什么不同,但它非常不同,因为在调用者可以查看其中的第一项之前,不必在内存中创建整个列表。
当调用者说:
每次循环需要一个新的 i 时,它都会在 Integers() 中运行更多的代码。 当该函数中的代码遇到
yield return
语句时,它会“暂停”。If you build and return a List (say it has 1 million elements), that's a big chunk of memory, and also of work to create it.
Sometimes the caller may only want to know what the first element is. Or they might want to write them to a file as they get them, rather than building the whole list in memory and then writing it to a file.
That's why it makes more sense to use yield return. It doesn't look that different to building the whole list and returning it, but it's very different because the whole list doesn't have to be created in memory before the caller can look at the first item on it.
When the caller says:
Each time the loop requires a new i, it runs a bit more of the code in Integers(). The code in that function is "paused" when it hits a
yield return
statement.Yield 允许您构建生成数据的方法,而无需在返回之前收集所有内容。 将其视为一路返回多个值。
这里有几个方法可以说明这一点,
这两种方法都不会将文件的全部内容读入内存,但您可以像这样使用它们:
Yield allows you to build methods that produce data without having to gather everything up before returning. Think of it as returning multiple values along the way.
Here's a couple of methods that illustrate the point
Neither of these two methods will read the whole contents of the file into memory, yet you can use them like this:
您可以使用
yield
构建任何迭代器。 这可能是一个延迟评估的系列(例如,从文件或数据库中读取行,而不是一次读取所有内容,这可能太多而无法保存在内存中),或者可能是迭代现有数据,例如 List< ;T>。C# in Depth 有一个免费章节 (6) 全部关于迭代器块。
我最近还博客介绍了如何使用
智能暴力算法的yield
。举个惰性文件读取器的例子:
这完全是“惰性的”; 在开始枚举之前,不会读取任何内容,并且内存中只保存一行。
请注意,LINQ-to-Objects 广泛使用了迭代器块 (
yield
)。 例如,Where
扩展本质上是:再次强调,完全惰性 - 允许您将多个操作链接在一起,而无需强制将所有内容加载到内存中。
You can use
yield
to build any iterator. That could be a lazily evaluated series (reading lines from a file or database, for example, without reading everything at once, which could be too much to hold in memory), or could be iterating over existing data such as aList<T>
.C# in Depth has a free chapter (6) all about iterator blocks.
I also blogged very recently about using
yield
for smart brute-force algorithms.For an example of the lazy file reader:
This is entirely "lazy"; nothing is read until you start enumerating, and only a single line is ever held in memory.
Note that LINQ-to-Objects makes extensive use of iterator blocks (
yield
). For example, theWhere
extension is essentially:And again, fully lazy - allowing you to chain together multiple operations without forcing everything to be loaded into memory.
Yield 允许您处理可能无限大小的集合,因为与基于列表的方法不同,整个集合永远不会一次性加载到内存中。 例如 IEnumerable<> 所有素数的数量可以通过寻找素数的适当算法来回退,而列表方法的大小总是有限的,因此是不完整的。 在此示例中,使用yield 还允许将下一个元素的处理推迟到需要时为止。
yield allows you to process collections that are potentially infinite in size because the entire collection is never loaded into memory in one go, unlike a List based approach. For instance an IEnumerable<> of all the prime numbers could be backed off by the appropriate algo for finding the primes, whereas a List approach would always be finite in size and therefore incomplete. In this example, using yield also allows processing for the next element to be deferred until it is required.
对我来说,一个真实的情况是,当我想要处理一个需要一段时间才能更顺利地填充的集合时。
想象一下这样的事情(伪代码):
在我可以开始处理其中的项目之前,不必等待一分钟来填充集合。 我将能够立即开始,然后在发生情况时向用户界面报告。
A real situation for me, is when i want to process a collection that takes a while to populate more smoothly.
Imagine something along the lines (psuedo code):
Instead of having to wait a minute for the collection to populate before i can start processing items in it. I will be able to start immediately, and then report back to the user-interface as it happens.
您可能并不总是想使用yield 而不是返回列表,并且在您的示例中,您使用yield 实际上返回整数列表。 根据您是否需要可变列表或不可变序列,您可以使用列表或迭代器(或其他一些可变/不可变集合)。
但使用收益率也有好处。
Yield 提供了一种构建惰性评估迭代器的简单方法。 (意味着当调用 MoveNext() 方法时,仅执行按顺序获取下一个元素的代码,然后迭代器返回,不再进行任何计算,直到再次调用该方法为止)
Yield 在幕后构建了一个状态机,并且这无需对通用生成器的状态进行编码,从而节省了您的工作量 => 更简洁/简单的代码。
Yield 自动构建优化且线程安全的迭代器,让您无需了解如何构建它们的详细信息。
Yield 比乍一看要强大得多,并且不仅仅可以用于构建简单的迭代器,请观看此视频以了解 Jeffrey Richter 和他的 AsyncEnumerator 以及如何使用 Yield 使使用异步模式的编码变得容易。
You may not always want to use yield instead of returning a list, and in your example you use yield to actually return a list of integers. Depending on whether you want a mutable list, or a immutable sequence, you could use a list, or an iterator (or some other collection muttable/immutable).
But there are benefits to use yield.
Yield provides an easy way to build lazy evaluated iterators. (Meaning only the code to get next element in sequence is executed when the MoveNext() method is called then the iterator returns doing no more computations, until the method is called again)
Yield builds a state machine under the covers, and this saves you allot of work by not having to code the states of your generic generator => more concise/simple code.
Yield automatically builds optimized and thread safe iterators, sparing you the details on how to build them.
Yield is much more powerful than it seems at first sight and can be used for much more than just building simple iterators, check out this video to see Jeffrey Richter and his AsyncEnumerator and how yield is used make coding using the async pattern easy.
您可能想要迭代各种集合:
You might want to iterate through various collections:
我同意每个人在这里所说的关于惰性求值和内存使用的所有内容,并且想添加另一个场景,在该场景中我发现使用
yield
关键字的迭代器很有用。 我遇到过一些情况,我必须对某些数据进行一系列可能昂贵的处理,而使用迭代器非常有用。 我可以简单地使用类似这样的迭代器,而不是立即处理整个文件,或者滚动我自己的处理管道:这里的优点是,通过将输入和输出保留到所有函数
IEnumerable
,我的处理管道是完全可组合的,易于阅读,并且延迟评估,所以我只需要做我真正需要做的处理。 这让我可以将几乎所有处理都放在 GUI 线程中,而不会影响响应能力,因此我不必担心任何线程问题。I agree with everything everyone has said here about lazy evaluation and memory usage and wanted to add another scenario where I have found the iterators using the
yield
keyword useful. I have run into some cases where I have to do a sequence of potentially expensive processing on some data where it is extremely useful to use iterators. Rather than processing the entire file immediately, or rolling my own processing pipeline, I can simply use iterators something like this:The advantage here is that by keeping the input and output to all of the functions
IEnumerable<double>
, my processing pipeline is completely composable, easy to read, and lazy evaluated so I only have to do the processing I really need to do. This lets me put almost all of my processing in the GUI thread without impacting responsiveness so I don't have to worry about any threading issues.我想出这个来克服 .net 必须手动深度复制列表的缺点。
我使用这个:
在另一个地方:
我试图想出一个oneliner来做到这一点,但这是不可能的,因为yield在匿名方法块内不起作用。
编辑:
更好的是,使用通用列表克隆器:
I came up with this to overcome .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 generic List cloner:
yield
使用的通过动态处理项目来节省内存的方法很好,但实际上它只是语法糖。 它已经存在很长时间了。 在任何具有函数或接口指针的语言(甚至 C 和汇编语言)中,您都可以使用回调函数/接口获得相同的效果。这个奇特的东西:
基本上等同于老式的:
The method used by
yield
of saving memory by processing items on-the-fly is nice, but really it's just syntactic sugar. It's been around for a long time. In any language that has function or interface pointers (even C and assembly) you can get the same effect using a callback function / interface.This fancy stuff:
is basically equivalent to old-fashioned: