C# 中的 ForEach 循环2024 年 08 月 29 日 | 阅读 9 分钟 C# 中的 foreach 循环 旨在简化遍历集合元素的过程,例如 数组、列表、字典 和实现 IEnumerable 或 IEnumerable 接口的其他数据结构。它提供了一种方便高效的方式来访问和处理集合中的每个项目。要理解 C# 中 foreach 循环背后的理论,请考虑以下关键点 枚举: foreach 循环 的核心是基于枚举器的迭代机制。它使用一个枚举器,这是一个对象,它跟踪集合中的当前位置,并允许您一个接一个地检索元素。枚举器通过 GetEnumerator() 方法 从集合中获取。 只读: foreach 循环 是只读的,这意味着它提供对集合中元素的只读访问。您可以检索和检查元素,但不能直接在循环中修改它们。尝试修改元素将导致编译错误。 自动初始化和清理: foreach 循环 会自动初始化枚举器,遍历集合,并在循环结束或发生异常时清理资源。这种自动管理简化了迭代过程,并降低了资源泄漏的风险。 类型推断: var 关键字 经常用于 foreach 循环声明中,以让编译器推断当前元素的类型。 语法它具有以下语法: foreach: 它是启动循环的关键字。 var item: 它是 局部变量(此处的 item)的声明,表示每次迭代中集合中的当前元素。var 关键字用于隐式类型,允许 C# 根据集合中元素的类型推断 item 的数据类型。 in collection: 它指定要迭代的集合。collection 应该是一个实现 IEnumerable 或 IEnumerable 接口 的对象,其中包括 C# 中大多数集合类型,例如数组、列表、字典等。 用花括号 {} 括起来的代码块: 它是 foreach 循环 的主体,您可以在其中放置要为集合中的每个元素执行的代码。 程序: 使用泛型 输出 Stud?nt: Alic?
Ent?r grad?s (comma-s?parat?d): 2,3
Av?rag? Grad?: 2.50
Stud?nt: Bob
Ent?r grad?s (comma-s?parat?d):6,8
Av?rag? Grad?: 8.00
Stud?nt: Charli?
Ent?r grad?s (comma-s?parat?d): 5,6,8
Av?rag? Grad?: 6.33
说明ForEach 循环(外层循环) 外层 foreach 循环 遍历学生列表中的每个学生。 对于每个学生,它执行以下任务 学生信息输入 - 它显示学生的姓名。
- 提示用户输入学生的成绩(逗号分隔)。
- 读取输入的成绩作为字符串,并使用 Split(',') 分割以获取字符串数组。
内层 ForEach 循环(成绩输入) - 内层 foreach 循环遍历用户输入的每个成绩字符串。
- 它尝试使用 int.TryParse 将每个成绩字符串解析为整数。
- 如果解析成功,它使用 AddGrade 方法 将解析后的成绩添加到学生的成绩列表中。
平均成绩计算 - 在输入完学生的所有成绩后,它使用 GetAverageGrade 方法 计算平均成绩。
- 之后,平均成绩以两位小数显示给用户 ({averageGrade:F2})。
为每个学生重复 外层 foreach 循环 继续,直到处理完列表中的所有学生。 复杂性分析时间复杂度 - 创建 n 个学生的列表的 时间复杂度 为 O(n),其中 n 是学生的数量。
- 外层 foreach 循环 遍历学生列表中的每个学生。由于有 n 个学生,此循环的时间复杂度为 O(n)。
- 内层 foreach 循环遍历每个学生输入的成绩。在最坏的情况下,如果一个学生有 m 门成绩,此循环在外层循环中的时间复杂度变为 O(m),其中 m 表示所有学生中最大成绩数。
- 计算每个学生的平均成绩涉及遍历他们的成绩。如果一个学生有 m 门成绩,此计算的时间复杂度为 O(m)。
- 整个程序的时间复杂度可以表示为 O(n * m),其中 n 是学生的数量,m 是所有学生中最大成绩数。
空间复杂度 - 学生列表的空间复杂度为 O(n),其中 n 是学生的数量。
- 程序创建 n 个 Student 类 实例,其中每个实例包含学生的姓名、成绩列表以及一些额外的对象引用开销。这些对象的空间复杂度为 O(n)。
- 每个学生都有一个成绩列表。所有成绩列表的空间复杂度为 O(m),其中 m 是所有学生中最大成绩数。
- 程序使用一些临时变量,如 averageGrade、gradeStrArray 和 grade。这些变量的空间复杂度相对较小,可以认为是 O(1)。
- 程序的空间复杂度可以近似为 *O(n + m)*,其中 n 是学生的数量,m 是所有学生中最大成绩数。
程序: 使用列表 输出 Long?st word: banana
Short?st word: fig
说明- 程序首先创建一个名为 words 的字符串列表。此列表包含五个单词:"apple"、"banana"、"cherry"、"date" 和 "fig"。
- 两个字符串变量 longest 和 shortest 被初始化。longest 将存储找到的最长单词,而 shortest 最初假设列表中的第一个单词是最短的。
- 程序使用 foreach 循环 遍历 words 列表中的每个单词。对于 每次迭代,当前单词由变量 word 表示。
- 在循环内部,程序将每个单词的长度与到目前为止找到的 最长 和 最短 单词的长度进行比较。如果当前单词比当前最长单词长,则更新 longest。同样,如果当前单词比当前最短单词短,则更新 shortest。
- foreach 循环结束后,程序将最长和最短的单词打印到控制台。
复杂性分析时间复杂度 - 创建包含 n 个字符串的列表的时间复杂度为 O(n),其中 n 是列表中单词的数量。
- 初始化 longest 和 shortest 变量需要恒定时间,O(1)。
- foreach 循环遍历 words 列表中的每个单词一次。由于有 n 个单词,此循环的时间复杂度为 O(n)。
- 在循环内部,程序比较每个单词的长度。这些比较是常数时间操作,O(1)。打印结果涉及常数时间操作,O(1)。
- 整个程序的时间复杂度由 foreach 循环决定,使其为 O(n),其中 n 是列表中单词的数量。
空间复杂度 - words 列表的空间复杂度为 O(n),其中 n 是列表中单词的数量。
- 这些变量的空间复杂度为 O(1),因为它们只保存对字符串的引用,不依赖于输入的大小。
- 程序使用少量临时变量,例如 word,它们具有恒定的空间复杂度,O(1)。
- 程序的空间复杂度为 O(n),主要是由于存储单词列表所需的空间。其他变量和临时存储不会显著影响整体空间复杂度。
程序: 枚举字典 输出 Alic?: 25 y?ars old
Bob: 30 y?ars old
Charli?: 28 y?ars old
说明- 程序首先创建一个名为 ages 的 Dictionary。此字典旨在存储键值对,其中键是 字符串(表示姓名),值是 整数(表示年龄)。
- 使用花括号 {} 语法在 Dictionary 初始化器 中将三个键值对添加到 ages 字典。
- 程序的主要部分是 foreach 循环,它遍历 ages 字典。
- foreach 循环的设置如下:foreach (var pair in ages)。
- 在循环的每次迭代中,循环变量 pair 表示 ages 字典中的一个键值对。
- 循环继续 执行,直到它遍历了字典中的所有键值对。
- 在循环内部,我们从 pair 变量中提取 键(姓名) 和 值(年龄)。
- Key 检索 键(姓名),它是一个字符串,并将其分配给 name 变量。
- Value 检索 值(年龄),它是一个整数,并将其分配给 age 变量。
- 在提取 键(姓名) 和 值(年龄) 后,程序使用 Console.WriteLine 以人类可读的格式显示此信息。
- 它使用 name 和 age 变量以 "姓名:年龄 岁" 的格式打印每个人的姓名和年龄。
- foreach 循环 对 ages 字典中的每个键值对重复此过程。
- 在此示例中,它将迭代三次(字典中每个人一次)。
复杂性分析时间复杂度 - 用 n 个键值对初始化字典的时间复杂度为 O(n),其中 n 是键值对的数量。
- foreach 循环 遍历字典中的所有键值对一次。由于有 n 个键值对,此循环的时间复杂度为 O(n)。
- 使用 Key 和 pair.Value 操作访问每个键值对的键和值是常数时间操作,O(1),因为它不依赖于输入的大小。
- 每个键值对的 Console.WriteLine 操作也是常数时间操作,O(1)。
- 整个程序的时间复杂度由 foreach 循环决定,使其为 O(n),其中 n 是字典中键值对的数量。
空间复杂度 - 字典本身的空间复杂度为 O(n),因为它存储 n 个键值对。
- 这些变量的空间复杂度为 O(1),因为它们只保存对现有数据的引用,并且它们消耗的内存量不依赖于输入的大小。
- 程序将字符串打印到控制台,这需要临时内存来存储这些字符串。然而,这些字符串使用的空间相对较小,可以认为是 O(1)。
- 程序的空间复杂度可以近似为 O(n),主要是由于存储字典所需的空间。其他变量和临时存储不会显著影响整体空间复杂度。
优点C# 中的 foreach 循环提供了多项优点,使其成为遍历集合、数组和可枚举数据结构的首选。以下是使用 foreach 循环的关键优势 优化: 现代 C# 编译器和运行时环境通常会优化 foreach 循环 以获得更好的性能。这种优化可以导致代码执行速度比手动管理索引或迭代器更快。 减少代码样板: 与传统的 for 循环相比,foreach 循环消除了手动管理循环变量、增量和边界检查的需要。它减少了代码的冗长和样板。 改进的错误处理: foreach 循环 有助于改进错误处理。通过抽象迭代逻辑,它减少了与其他循环结构常见的差一错误和与索引相关的错误的发生几率。 增强的代码可维护性: foreach 循环 简洁而富有表达力的特性使代码库更易于维护。开发人员花更少的时间调试与迭代相关的问题,使他们能够专注于更高级别的逻辑。 函数式编程支持:foreach 循环 通过推广声明式编码风格与函数式编程的原则保持一致。它可以产生更清晰、更易于维护的代码,强调需要做什么而不是如何做。
|