跳至页脚内容
Iron Academy Logo
学习 C#
学习 C#

其他类别

C# 中的 Yield 简介--它是什么、如何使用以及何时有用

Tim Corey
43分58秒

当你第一次接触 C# 中的 yield 关键字时,它可能会让你感到困惑。 它究竟是如何运作的? 何时应该使用 yield return 语句而不是传统的 return 语句? 为了全面理解,我们将根据 Tim Corey 出色的 YouTube 教程" C# 中的 Yield 简介 - 它是什么、如何使用以及何时有用"进行详细解释。

在本指南中,我们将通过时间戳引用 Tim 视频中的特定要点,以便于导航,并包含实际示例,以展示 yield 如何改变您处理数据流、大型集合和惰性求值的方式。

C# 中 Yield 关键字简介

Tim 首先介绍了 yield 关键字,并强调它对于初次接触它的开发者来说常常令人困惑。他解释说,yield 语句允许方法暂停执行,保持其状态,然后在下次调用时从中断的地方继续执行。 Tim 强调,了解产量对于高效处理数据至关重要,尤其是在处理大型数据集或实施自定义迭代逻辑时。

设置一个简单的示例:类程序和静态 void main 函数

为了消除干扰,Tim 在 Visual Studio 中创建了一个简单的控制台应用程序,名为"YieldDemoApp"。

Understanding Yield In Csharp 1 related to 设置一个简单的示例:类程序和静态 void main 函数

C# 中 yield 的实际作用是什么

蒂姆随后深入探讨了理论。 在 2:04,他描述了 yield 的行为:yield 语句不会一次性处理整个集合,而是会保留一个位置——就像把拇指放在书里一样——以便执行可以暂停并在以后恢复。

这种行为对于延迟执行至关重要,延迟执行中,值仅在需要时生成,而不是预先计算所有内容。 Tim 的描述清晰地奠定了理解收益率如何运作的基础。

编写演示代码

在类 Program 的静态 void Main 方法中,他使用 Console.WriteLine 设置了"应用程序开始"和"应用程序结束"等基本首尾消息,这有助于在稍后使用 foreach 循环时清晰地可视化流程。

这个初始代码示例完全专注于 yield 的实现,不涉及任何 UI 复杂性。

创建 PersonModel 类

为了演示,Tim 创建了一个名为 PersonModel 的类,该类具有 FirstName 和 LastName 属性以及一个构造函数。 创建 PersonModel 对象时,会打印一条消息,指示初始化的是哪个用户。 这有助于直观地了解对象何时被创建,何时被消耗。

Understanding Yield In Csharp 2 related to 创建 PersonModel 类

这个简单的生成代码步骤为使用自定义迭代器奠定了基础。

使用传统列表返回构建数据访问类

5:06,Tim 转到 DataAccess 类,该类有一个 GetPeople 方法,返回 IEnumerable 对象。。 最初,返回值是一个列表,其中包含三个 PersonModel 实例:Tim Corey、Sue Storm 和 Jane Smith。

Understanding Yield In Csharp 3 related to 使用传统列表返回构建数据访问类

这种迭代器方法会在迭代开始之前立即将所有对象加载到内存中——这一点蒂姆后来将其与使用 yield 进行了对比。

Understanding Yield In Csharp 4 related to 使用传统列表返回构建数据访问类

用列表演示内存使用情况

运行 foreach 循环后,Tim 发现,在读取第一个元素之前,所有三个用户都已经创建好了。 这凸显了处理大型集合或大型数据集时可能出现的问题——内存使用率高。

Understanding Yield In Csharp 5 related to 用列表演示内存使用情况

Tim 解释说,如果我们有一千个用户,即使我们只需要几个用户,我们也会创建一千个对象,导致内存分配效率低下。

将递延执行改为收益回报

10:01,Tim 修改了 GetPeople 函数,使其使用 yield return 而不是创建临时集合(一个 List)。 每个 yield return 语句都会一次直接输出一个 PersonModel。

方法内部的这段关键代码实现了延迟求值,只有当 foreach 循环需要时才会生成下一个元素。

Tim 还澄清说,该方法的返回类型必须是 IEnumerable。而使用列表则会违背延迟执行的初衷。

调试:编译器如何生成 yield 代码

Tim 使用断点来逐步完成整个过程。 他证明,在第一次调用 MoveNext 之前,序列为空。 只有当 foreach 需要下一个值时,编译器才会触发迭代器块并执行 yield return num 行,该行会初始化并返回 PersonModel。

Understanding Yield In Csharp 6 related to 调试:编译器如何生成 yield 代码

Tim 强调,编译器会在底层生成特殊的状态机来管理暂停和恢复的执行。

利用收益率的益处和效率

蒂姆解释了为什么产量如此高效:

您可以一次获取一条记录。

您可以限制所需的整数数量。

  • 避免将整个集合加载到内存中。

  • 提高了可扩展性,尤其是在处理大型文件或数据流时。

通过使用 LINQ 的 .Take(2),Tim 演示了即使存在三个 yield return 语句,也只初始化了两个对象——突出了延迟执行的实际应用。

实际示例:利用收益率生成素数

Tim 创建了一个新的静态 IEnumerable。Generators 类中的 GetPrimeNumbers() 方法。 在这个无限循环中,Tim 使用辅助函数 IsPrimeNumber(int number) 来检查素数。 如果该数是质数,他使用 yield return number 将其输出。

这个例子表明,产量对于安全地处理无限序列至关重要。

如果没有 yield,代码会因为无限消耗内存而崩溃。 然而,利用收益率,延迟执行可以确保按需生成数字。

使用 Take() 获取素数

Tim 接着展示了如何安全地获取固定数量的质数:

var primeNumbers = Generators.GetPrimeNumbers().Take(10000);
var primeNumbers = Generators.GetPrimeNumbers().Take(10000);

这段写在 static void Main 中的代码可以高效地获取 10,000 个素数。

由于收益率是逐个产生数字的,因此内存使用率很低。

自定义迭代:使用 GetEnumerator 和 MoveNext

Tim 更进一步,解释了如何手动控制迭代。

他创建了一个变量 iterator = primeNumbers.GetEnumerator(),然后使用 int i 的 for 循环,并调用 iterator.MoveNext() 手动获取元素。

这种手动方法可以实现自定义迭代——仅在需要时请求下一个值,并且表明该方法会从上次暂停的地方恢复执行。

常见错误:对已生成的集合使用 ToList()

36:45 时,Tim 警告:使用 .ToList() 将 yield 序列转换为 List 会导致立即进行完整求值。

如果对无限序列调用 .ToList(),则可能会导致应用程序崩溃。

Tim 强调 yield return 的目的是为了延迟求值,而调用 .ToList() 会强制执行完全内存实例化,从而打破这种模式。

在使用 LINQ 方法时,Tim 建议谨慎引入 .ToList()。

摘要:为什么收益率是一个强大的工具

Tim 最后强调,虽然 yield 关键字会增加一些开销(维护一个状态机),但它能减少内存使用量并实现惰性求值,因此在处理以下情况时,它是一个强大的工具:

  • 大型数据集

  • 大文件

  • 自定义迭代器

  • 无限序列

数据流

  • 延迟处理

他建议在不同的项目中练习收益率,以加深对收益率回报的理解,并避免常见的陷阱。

最后,Tim邀请观众分享他们在生产代码中如何使用yield。

结论

通过观看 Tim Corey 的视频,我们已经了解了 yield 关键字给 C# 带来的巨大好处。 从创建自定义迭代器到高效管理大型集合,yield 返回使函数返回更加智能、更加节省内存。 无论你处理的是变量 number、变量 point、变量 reader、变量 connection 还是大型数据集,掌握 yield 都能极大地提升你的 C# 编码技能。

如果你之前没有探索过使用 yield,现在正是通过简单的示例来练习它,并更好地了解 yield return 在 C# 编译器中是如何工作的绝佳时机。 欢迎访问 Tim 的官方YouTube 频道,观看更多精彩视频。

Hero Worlddot related to C# 中的 Yield 简介--它是什么、如何使用以及何时有用
Hero Affiliate related to C# 中的 Yield 简介--它是什么、如何使用以及何时有用

分享您的所爱,赚取更多收入

您为使用 .NET、C#、Java、Python 或 Node.js 的开发人员创建内容吗?将您的专业知识转化为额外收入!

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我