为了揭开神秘面纱,我将避免谈论迭代器,因为它们本身也可能成为谜团的一部分。
yield return 和yield break 语句最常用于提供集合的“延迟评估”。
这意味着当您获取使用 yield return 的方法的值时,您尝试获取的东西的集合还不存在(它本质上是空的)。当您遍历它们(使用 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[] 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.");