C#中foreach实例代码怎么写才规范且高效

来源:AI视频音频作者:叶知晏头衔:草根站长
导读:本期聚焦于叶知晏创作的《C#中foreach实例代码怎么写才规范且高效》,敬请观看详情。在遍历集合时直接修改元素会引发InvalidOperationException,这是不少C#初学者踩过的坑。foreach本质是调用集合的GetEnumerator方法,通过MoveNext和Current完成迭代,并非简单的for循环语法糖。本文结合List、字典和自定义类型的代码示例,说明foreach的正确写法、不能修改集合的原因,以及如何用yield return实现惰性枚举提升内存效率。掌握这些细节能避免运行时异常,也让代码可读性更强。

在C#开发中,foreach是最常用的遍历语法之一。它不仅能遍历数组和List,还能操作实现了IEnumerable接口的任何类型。理解其底层机制和书写规范,是写出健壮代码的基础。下面通过具体场景和代码示例,系统讲解foreach的使用方式与注意事项。

C#中foreach实例代码怎么写才规范且高效

foreach的基础语法与遍历List示例

foreach的语法结构非常直观:foreach (var item in collection)。其中collection必须是实现了IEnumerable或IEnumerable<T>接口的对象。编译器在背后会将其转换为对枚举器的调用,先获取GetEnumerator(),然后在循环中不断执行MoveNext()并读取Current属性,直到返回false。

下面是一个遍历List<string>的基础例子,展示如何安全地读取每个元素并打印:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
        foreach (string name in names)
        {
            Console.WriteLine("姓名: " + name);
        }
    }
}

上述代码在编译后等价于使用while循环调用枚举器,但foreach版本可读性更高,且避免了手动管理索引越界的问题。对于值类型元素,foreach中的迭代变量是只读的副本;对于引用类型,迭代变量持有对象的引用,可以修改对象内部状态,但不能将迭代变量重新指向新对象。

遍历字典与自定义迭代器的实现

除了线性集合,foreach也常用于遍历Dictionary。字典的每一项为KeyValuePair<TKey, TValue>,通过var关键字可以让编译器自动推断类型,减少冗余声明。在遍历过程中如果需要同时拿到键和值,直接解构即可。

我们还可以让自定义类支持foreach,只需实现IEnumerable<T>接口并提供GetEnumerator方法。以下示例定义一个简单的整数范围类,并返回范围内的每个数:

using System;
using System.Collections;
using System.Collections.Generic;

class IntRange : IEnumerable<int>
{
    private int start;
    private int end;

    public IntRange(int s, int e)
    {
        start = s;
        end = e;
    }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = start; i <= end; i++)
        {
            yield return i;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class Test
{
    static void Main()
    {
        IntRange range = new IntRange(1, 3);
        foreach (int num in range)
        {
            Console.WriteLine(num);
        }
    }
}

这段代码使用了yield return语法,编译器会自动生成一个状态机枚举器,实现惰性迭代。也就是说,每次循环才计算下一个值,而不是一次性把所有数据装入内存。对于大型数据集合或流式处理,这种方式能显著降低内存占用。

字典遍历时还有一点需要注意:如果在foreach中尝试对字典本身进行增删操作,同样会抛出运行时异常。若必须修改,应先将键或值复制到临时列表中,再在循环外更新原字典。自定义迭代器如果依赖内部状态,也应在设计阶段就明确是否允许并发修改。

foreach中的常见错误与性能优化

最常见的错误是在foreach循环体内直接对集合调用Add或Remove。由于枚举器会跟踪集合版本号,任何结构性修改都会使版本号变化,从而触发InvalidOperationException。例如对List进行移除操作应使用倒序for循环,或借助RemoveAll方法。

从性能角度看,foreach对数组的遍历会被JIT优化为和for循环相近的机器码,几乎无额外开销;但对ArrayList等非泛型集合,由于涉及装箱拆箱,性能不如泛型List配合foreach。此外,如果迭代逻辑非常简单且需要索引,for循环可能更直观。下面展示一个错误示例及正确做法:

using System;
using System.Collections.Generic;

class Demo
{
    static void Main()
    {
        List<int> nums = new List<int> { 1, 2, 3, 4 };
        // 错误:在foreach中删除
        // foreach (int n in nums)
        // {
        //     if (n % 2 == 0) nums.Remove(n);
        // }

        // 正确:使用RemoveAll
        nums.RemoveAll(n => n % 2 == 0);
        foreach (int n in nums)
        {
            Console.WriteLine(n);
        }
    }
}

合理利用foreach不仅能减少bug,还能借助LINQ实现声明式数据处理。例如foreach (var x in list.Where(i => i > 0))可以清晰表达过滤意图。但需记住,LINQ查询默认也是延迟执行,只有在foreach迭代时才真正计算,因此要避免在同一集合上多次遍历导致重复计算,必要时用ToList缓存结果。

最后,在异步场景中不要在foreach里直接调用不含等待的异步方法而忽略Task,否则可能丢失异常或造成并发问题。若需并行处理,可考虑Parallel.ForEach,但要确保操作是线程安全的。掌握这些细节,才能把C#中foreach实例代码写得既规范又高效。

C#foreach迭代器修改时间:2026-08-17 16:44:13

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。