在 C# 中使用的 yield 关键字是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39476/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the yield keyword used for in C#?
提问by Herms
In the How Can I Expose Only a Fragment of IList<>question one of the answers had the following code snippet:
在How Can I Expose Only a Fragment of IList<>问题中,答案之一具有以下代码片段:
IEnumerable<object> FilteredList()
{
foreach(object item in FullList)
{
if(IsItemInPartialList(item))
yield return item;
}
}
What does the yield keyword do there? I've seen it referenced in a couple places, and one other question, but I haven't quite figured out what it actually does. I'm used to thinking of yield in the sense of one thread yielding to another, but that doesn't seem relevant here.
yield 关键字在那里做什么?我已经在几个地方和另一个问题中看到了它的引用,但我还没有完全弄清楚它的实际作用。我习惯于从一个线程屈服于另一个线程的意义上考虑屈服,但这在这里似乎无关紧要。
采纳答案by Mendelt
The yield
keyword actually does quite a lot here.
该yield
关键字实际上这里确实不少。
The function returns an object that implements the IEnumerable<object>
interface. If a calling function starts foreach
ing over this object, the function is called again until it "yields". This is syntactic sugar introduced in C# 2.0. In earlier versions you had to create your own IEnumerable
and IEnumerator
objects to do stuff like this.
该函数返回一个实现该IEnumerable<object>
接口的对象。如果调用函数开始调用foreach
此对象,则会再次调用该函数,直到它“产生”。这是C# 2.0 中引入的语法糖。在早期版本中,你必须创建自己的IEnumerable
,并IEnumerator
反对做这样的东西。
The easiest way understand code like this is to type-in an example, set some breakpoints and see what happens. Try stepping through this example:
理解这样的代码最简单的方法是输入一个示例,设置一些断点,然后看看会发生什么。尝试逐步执行此示例:
public void Consumer()
{
foreach(int i in Integers())
{
Console.WriteLine(i.ToString());
}
}
public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}
When you step through the example, you'll find the first call to Integers()
returns 1
. The second call returns 2
and the line yield return 1
is not executed again.
当您通过例如一步,你会发现第一个电话Integers()
的回报1
。第二个调用返回2
,该行yield return 1
不再执行。
Here is a real-life example:
这是一个现实生活中的例子:
public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
using (var connection = CreateConnection())
{
using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
{
command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return make(reader);
}
}
}
}
}
回答by Joel Coehoorn
Iteration. It creates a state machine "under the covers" that remembers where you were on each additional cycle of the function and picks up from there.
迭代。它创建了一个“幕后”状态机,它会记住您在函数的每个附加循环中所处的位置并从那里开始。
回答by Joel Coehoorn
It is a very simple and easy way to create an enumerable for your object. The compiler creates a class that wraps your method and that implements, in this case, IEnumerable<object>. Without the yield keyword, you'd have to create an object that implements IEnumerable<object>.
这是为您的对象创建可枚举的一种非常简单易行的方法。编译器创建一个类来包装您的方法,并在本例中实现 IEnumerable<object>。如果没有 yield 关键字,您就必须创建一个实现 IEnumerable<object> 的对象。
回答by aku
It's producing enumerable sequence. What it does is actually creating local IEnumerable sequence and returning it as a method result
它正在产生可枚举的序列。它所做的实际上是创建本地 IEnumerable 序列并将其作为方法结果返回
回答by Konrad Rudolph
Intuitively, the keyword returns a value from the function without leaving it, i.e. in your code example it returns the current item
value and then resumes the loop. More formally, it is used by the compiler to generate code for an iterator. Iterators are functions that return IEnumerable
objects. The MSDNhas several articlesabout them.
直观地说,关键字从函数返回一个值而不离开它,即在您的代码示例中,它返回当前item
值然后继续循环。更正式地说,编译器使用它来为迭代器生成代码。迭代器是返回IEnumerable
对象的函数。在MSDN有一些文章对他们。
回答by Svend
Recently Raymond Chen also ran an interesting series of articles on the yield keyword.
最近,Raymond Chen 还发表了一系列关于 yield 关键字的有趣文章。
- The implementation of iterators in C# and its consequences (part 1)
- The implementation of iterators in C# and its consequences (part 2)
- The implementation of iterators in C# and its consequences (part 3)
- The implementation of iterators in C# and its consequences (part 4)
While it's nominally used for easily implementing an iterator pattern, but can be generalized into a state machine. No point in quoting Raymond, the last part also links to other uses (but the example in Entin's blog is esp good, showing how to write async safe code).
虽然它名义上用于轻松实现迭代器模式,但可以推广到状态机。引用 Raymond 没有意义,最后一部分还链接到其他用途(但 Entin 博客中的示例特别好,展示了如何编写异步安全代码)。
回答by Gishu
It's trying to bring in some Ruby Goodness :)
Concept:This is some sample Ruby Code that prints out each element of the array
它试图引入一些 Ruby Goodness :)
概念:这是一些示例 Ruby 代码,可以打印出数组的每个元素
rubyArray = [1,2,3,4,5,6,7,8,9,10]
rubyArray.each{|x|
puts x # do whatever with x
}
The Array's each method implementation yieldscontrol over to the caller (the 'puts x') with eachelement of the array neatly presented as x. The caller can then do whatever it needs to do with x.
阵列的每个方法实现的产率控制到呼叫者(即“放X”)与每个所述阵列的元件整齐地呈现为×。然后调用者可以对 x 做任何需要做的事情。
However .Netdoesn't go all the way here.. C# seems to have coupled yield with IEnumerable, in a way forcing you to write a foreach loop in the caller as seen in Mendelt's response. Little less elegant.
然而,.Net并没有完全走到这里.. C# 似乎将 yield 与 IEnumerable 结合起来,以某种方式迫使您在调用方中编写 foreach 循环,如 Mendelt 的响应中所见。少一点优雅。
//calling code
foreach(int i in obCustomClass.Each())
{
Console.WriteLine(i.ToString());
}
// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
for(int iLooper=0; iLooper<data.Length; ++iLooper)
yield return data[iLooper];
}
回答by BoiseBaked
The C# yield keyword, to put it simply, allows many calls to a body of code, referred to as an iterator, that knows how to return before it's done and, when called again, continues where it left off - i.e. it helps an iterator become transparently stateful per each item in a sequence that the iterator returns in successive calls.
简而言之,C# yield 关键字允许对代码体(称为迭代器)进行多次调用,该代码体知道如何在完成之前返回,并在再次调用时从停止的地方继续——即它有助于迭代器在迭代器在连续调用中返回的序列中,每个项目都变得透明有状态。
In JavaScript, the same concept is called Generators.
在 JavaScript 中,相同的概念称为生成器。
回答by Shivprasad Ktheitroadala
回答by RKS
yield return
is used with enumerators. On each call of yield statement, control is returned to the caller but it ensures that the callee's state is maintained. Due to this, when the caller enumerates the next element, it continues execution in the callee method from statement immediately after the yield
statement.
yield return
与枚举器一起使用。在每次调用 yield 语句时,控制权都返回给调用者,但它确保保持被调用者的状态。因此,当调用者枚举下一个元素时,它会在紧跟在该yield
语句之后的语句中继续在被调用方方法中执行。
Let us try to understand this with an example. In this example, corresponding to each line I have mentioned the order in which execution flows.
让我们试着用一个例子来理解这一点。在这个例子中,对应每一行,我已经提到了执行流程的顺序。
static void Main(string[] args)
{
foreach (int fib in Fibs(6))//1, 5
{
Console.WriteLine(fib + " ");//4, 10
}
}
static IEnumerable<int> Fibs(int fibCount)
{
for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
{
yield return prevFib;//3, 9
int newFib = prevFib + currFib;//6
prevFib = currFib;//7
currFib = newFib;//8
}
}
Also, the state is maintained for each enumeration. Suppose, I have another call to Fibs()
method then the state will be reset for it.
此外,为每个枚举维护状态。假设,我有另一个Fibs()
方法调用,然后状态将被重置。