有人可以揭开yield关键字吗?

我在Stack Overflow和博客中看到yield关键字被大量使用。 我不使用LINQ。 有人可以解释yield关键字吗?

我知道存在类似的问题。 但没有一个真正解释它在纯朴的简单语言中的用途。


迄今为止,对此的最好解释(我见过)是Jon Skeet的书 - 而这一章是免费的! 第6章,C#深入。 这里没有任何我可以添加的内容。

然后购买这本书; 你会成为一个更好的C#程序员。


问:为什么我没有在这里写更长的答案(从评论中转述); 简单。 正如Eric Lippert所观察到的(在这里), yield结构(和它背后的魔力)是C#编译器中最复杂的一部分代码,试图在简短的回复中对它进行描述,这在最好情况下是天真的。 有很多细微之处可以yield海事组织,最好是指一个预先存在的(和完全合格的)资源。

埃里克的博客现在有7个条目(这只是最近的)讨论yield 。 我对Eric有着很大的尊重,但他的博客可能更适合作为对这个主题yield人的“更多信息”(在这种情况下是yield ),因为它通常描述了很多背景设计考虑因素。 最好在合理的基础上完成。

(是的,第6章确实下载了;我证实了......)


yield关键字与返回IEnumerable<T>IEnumerator<T> ,它使编译器生成一个实现使用迭代器所需的管道的类。 例如

public IEnumerator<int> SequenceOfOneToThree() {
    yield return 1;
    yield return 2;
    yield return 3;
}

鉴于上述情况,编译器将生成一个实现IEnumerator<int>IEnumerable<int>IDisposable (实际上它也将实现IEnumerableIEnumerator的非泛型版本)的类。

这允许你像这样在foreach循环中调用SequenceOfOneToThree方法

foreach(var number in SequenceOfOneToThree) {
    Console.WriteLine(number);
}

迭代器是一个状态机,所以每次调用yield都会记录该方法中的位置。 如果迭代器被移动到下一个元素,该方法在该位置之后立即恢复。 所以第一次迭代返回1并标记该位置。 下一个迭代器在1之后继续,因此返回2等等。

不用说,你可以用你喜欢的任何方式生成序列,所以你不必像我那样对数字进行硬编码。 另外,如果你想打破循环,你可以使用yield break


为了揭开神秘面纱,我会避免谈论迭代器,因为它们本身可能是神秘的一部分。

收益率收益率和收益率收益率报告通常用于提供收集的“延期评估”。

这意味着当你获得使用yield return的方法的值时,你试图获得的东西的集合并不存在(它基本上是空的)。 当你循环它们(使用foreach)时,它将在那时执行该方法并获取枚举中的下一个元素。

某些属性和方法会导致整个枚举一次被评估(如“计数”)。

以下是返回收集和返回收益之间区别的一个简单示例:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

// we're going to execute the GetYieldEnumerable() method
// but the foreach statement inside it isn't going to execute
var yieldNames = GetNamesEnumerable();

// now we're going to execute the GetList() method and
// the foreach method will execute
var listNames = GetList();

// now we want to look for a specific name in yieldNames.
// only the first two iterations of the foreach loop in the 
// GetYieldEnumeration() method will need to be called to find it.
if (yieldNames.Contains("Jim")
    Console.WriteLine("Found Jim and only had to loop twice!");

// now we'll look for a specific name in listNames.
// the entire names collection was already iterated over
// so we've already paid the initial cost of looping through that collection.
// now we're going to have to add two more loops to find it in the listNames
// collection.
if (listNames.Contains("Jim"))
    Console.WriteLine("Found Jim and had to loop 7 times! (5 for names and 2 for listNames)");

如果您需要在源数据具有值之前获取对枚举的引用,则也可以使用它。 例如,如果名称集合尚未完成,则以下列开头:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

var yieldNames = GetNamesEnumerable();

var listNames = GetList();

// now we'll change the source data by renaming "Jim" to "Jimbo"
names[1] = "Jimbo";

if (yieldNames.Contains("Jimbo")
    Console.WriteLine("Found Jimbo!");

// Because this enumeration was evaluated completely before we changed "Jim"
// to "Jimbo" it isn't going to be found
if (listNames.Contains("Jimbo"))
    // this can't be true
else
   Console.WriteLine("Couldn't find Jimbo, because he wasn't there when I was evaluated.");
链接地址: http://www.djcxy.com/p/9107.html

上一篇: Can someone demystify the yield keyword?

下一篇: Odd return syntax statement