列表或数组实现会立即加载所有项目,而yield实现提供了延迟执行的解决方案。
在实践中,通常希望尽可能少地执行工作,以减少应用程序的资源消耗。
例如,我们可能有一个从数据库处理数百万条记录的应用程序。当我们在延迟执行的拉取模型中使用IEnumerable时,可以获得以下好处:
- 可扩展性、可靠性和可预测性可能会提高,因为记录的数量不会显著影响应用程序的资源需求。
- 性能和响应性可能会提高,因为处理可以立即开始,而不是等待整个集合首先加载完毕。
- 可恢复性和利用率可能会提高,因为应用程序可以停止、启动、中断或失败。与预取所有数据相比,只有正在进行的项目将丢失,而实际上只使用了部分结果。
- 连续处理在添加恒定工作负载流的环境中是可能的。
下面是建立集合和使用yield之间的比较。
列表示例
public class ContactListStore : IStore<ContactModel>
{
public IEnumerable<ContactModel> GetEnumerator()
{
var contacts = new List<ContactModel>();
Console.WriteLine("ContactListStore: Creating contact 1");
contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
Console.WriteLine("ContactListStore: Creating contact 2");
contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
Console.WriteLine("ContactListStore: Creating contact 3");
contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
return contacts;
}
}
static void Main(string[] args)
{
var store = new ContactListStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection.");
Console.ReadLine();
}
控制台输出
ContactListStore: 创建联系人1
ContactListStore: 创建联系人2
ContactListStore: 创建联系人3
准备遍历整个集合。
注意:整个集合在没有请求列表中的任何一项情况下被加载到内存中。
yield示例
public class ContactYieldStore : IStore<ContactModel>
{
public IEnumerable<ContactModel> GetEnumerator()
{
Console.WriteLine("ContactYieldStore: Creating contact 1");
yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
Console.WriteLine("ContactYieldStore: Creating contact 2");
yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
Console.WriteLine("ContactYieldStore: Creating contact 3");
yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
}
}
static void Main(string[] args)
{
var store = new ContactYieldStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection.");
Console.ReadLine();
}
控制台输出
准备迭代整个集合。
注意:该集合并未被执行。这是IEnumerable的“延迟执行”特性所致。只有在真正需要时,才会构造一个项目。
让我们再次调用集合,并观察当我们获取集合中的第一个联系人时的行为。
static void Main(string[] args)
{
var store = new ContactYieldStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection");
Console.WriteLine("Hello {0}", contacts.First().FirstName);
Console.ReadLine();
}
控制台输出
准备遍历集合
ContactYieldStore: 创建联系人1
Hello Bob
太好了!当客户端从集合中“拉出”该项时,只有第一个联系人被构建。