C# 有人能揭开收益率关键字的神秘面纱吗?

C# 有人能揭开收益率关键字的神秘面纱吗?,c#,language-features,yield,C#,Language Features,Yield,我在Stack Overflow和博客上看到过很多使用yield关键字的情况。我不使用。有人能解释一下收益率关键字吗 我知道也存在类似的问题。 但没有人能用简单明了的语言真正解释它的用途。到目前为止,对这一点(我所看到的)最好的解释是乔恩·斯基特的书——这一章是免费的!第六章。我在这里没有什么可以补充的 然后买这本书;你会因此成为一名更好的C#程序员 问:为什么我不在这里写一个较长的答案(根据评论转述);简单。正如Eric Lippert()所观察到的,yield构造(以及它背后的魔力)是C#

我在Stack Overflow和博客上看到过很多使用yield关键字的情况。我不使用。有人能解释一下收益率关键字吗

我知道也存在类似的问题。
但没有人能用简单明了的语言真正解释它的用途。

到目前为止,对这一点(我所看到的)最好的解释是乔恩·斯基特的书——这一章是免费的!第六章。我在这里没有什么可以补充的

然后买这本书;你会因此成为一名更好的C#程序员


问:为什么我不在这里写一个较长的答案(根据评论转述);简单。正如Eric Lippert()所观察到的,
yield
构造(以及它背后的魔力)是C#编译器中最复杂的一段代码,在这里尝试用一个简短的回答来描述它充其量是幼稚的。
收益率有很多细微差别,因此IMO最好参考预先存在的(完全限定的)资源

Eric的博客现在有7条(这只是最近的一条)讨论
yield
。我对Eric非常尊重,但他的博客可能更适合作为“进一步信息”给那些对这个主题感到满意的人(在本例中,
yield
),因为它通常描述了很多背景设计考虑因素。最好是在合理的基础上完成的。< /P> (是的,第6章确实下载了;我验证了…

埃里克·怀特的《这本书》非常值得一读,但它的解释和我看到的一样清晰。

看看文档和示例。这本质上是一种在C#中创建迭代器的简单方法

公共类列表
{
//使用系统集合;
公共静态IEnumerable幂(整数,整数指数)
{
int计数器=0;
int结果=1;
while(计数器+++<指数)
{
结果=结果*编号;
收益结果;
}
}
静态void Main()
{
//显示指数8之前的2的幂:
foreach(int i掌权(2,8))
{
Console.Write(“{0}”,i);
}
}
}

与LINQ没有直接关系,而是与。链接的MSDN详细介绍了该语言功能。请特别参阅本节。有关迭代器块的详细信息,请参阅Eric Lippert最近关于该功能的博客。有关一般概念,请参阅关于迭代器的Wikipedia。

关键字
yield
是编写
IEnumerator
的便捷方法。例如:

public static IEnumerator<int> Range(int from, int to)
{
    for (int i = from; i < to; i++)
    {
        yield return i;
    }
}
公共静态IEnumerator范围(int-from,int-to)
{
for(int i=from;i
被C#编译器转换为类似于:

public static IEnumerator<int> Range(int from, int to)
{
    return new RangeEnumerator(from, to);
}

class RangeEnumerator : IEnumerator<int>
{
    private int from, to, current;

    public RangeEnumerator(int from, int to)
    {
        this.from = from;
        this.to = to;
        this.current = from;
    }

    public bool MoveNext()
    {
        this.current++;
        return this.current < this.to;
    }

    public int Current
    {
        get
        {
            return this.current;
        }
    }
}
公共静态IEnumerator范围(int-from,int-to)
{
返回新的RangeEnumerator(从,到);
}
类RangeEnumerator:IEnumerator
{
私有整数从,到,当前;
公共范围枚举器(int-from,int-to)
{
this.from=from;
这个;
这个。电流=来自;
}
公共图书馆
{
这个.current++;
返回this.current
关键字
yield
与返回
IEnumerable
IEnumerator
的方法一起使用,它使编译器生成一个类,实现使用迭代器所需的管道。例如

public IEnumerator<int> SequenceOfOneToThree() {
    yield return 1;
    yield return 2;
    yield return 3;
}
迭代器是一个状态机,因此每次调用
yield
时,都会记录方法中的位置。如果迭代器移动到下一个元素,该方法将在该位置之后立即恢复。因此,第一次迭代返回1并标记该位置。下一个迭代器在1之后立即恢复,因此返回2,以此类推


不用说,您可以以任何喜欢的方式生成序列,因此您不必像我一样硬编码数字。另外,如果你想打破循环,你可以使用
屈服中断

让我补充一下。收益率不是一个关键词。 它只有在使用“收益率回报”时才有效,而不是像正常变量一样有效

它用于从函数返回迭代器。你可以进一步搜索。
我建议搜索“Returning Array vs Iterator”(返回数组vs Iterator)

,以消除神秘感,我将避免谈论迭代器,因为迭代器本身可能是谜团的一部分

收益率返回和收益率中断语句最常用于提供集合的“延迟评估”

这意味着,当您获取使用收益率返回的方法的值时,您试图获取的内容集合还不存在(基本上是空的)。当您循环遍历它们(使用foreach)时,它将在此时执行该方法并获取枚举中的下一个元素

某些属性和方法将导致立即计算整个枚举(例如“Count”)

下面是返回集合和返回收益之间区别的一个快速示例:

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[]name={“Joe”、“Jim”、“Sam”、“Ed”、“Sally”};
公共IEnumerable GetYieldEnumerable()
{
foreach(名称中的变量名称)
产生返回名称;
}
公共IEnumerable GetList()
{
var list=新列表();
foreach(名称中的变量名称)
列表。添加(名称);
退货清单;
}
//我们将执行GetYieldEnumerable()方法
//但是它里面的foreach语句不会执行
var yieldNames=GetNamesEnumerable();
//现在我们要执行GetList()方法
//将执行foreach方法
var listNames=GetList();
//现在我们想在yieldNames中查找一个特定的名称。
//仅在
//需要调用GetYieldEnumeration()方法才能找到它。
if(yieldNames.Contains)(“Jim”)
foreach(var number in SequenceOfOneToThree) {
    Console.WriteLine(number);
}
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.");
static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}
public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}
class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}