C# 中 foreach 循环和 for 循环深度比较

简介: 为什么建议你多数情况下使用 foreach 进行遍历循环?看完你就明白了

Coding-80.png

前言

嘿,小伙伴们!

如果你最近读过我的另一篇文章《C# 去掉字符串最后一个字符的 4 种方法》,你可能会对 foreach 循环和 for 循环这两者之间的微妙差异产生了一些好奇。

今天,咱们就来聊聊这两位循环界的 "老炮儿" —— foreach 循环和和 for 循环,看看它们到底有何不同!

foreach 循环的内部实现原理

foreach 循环是 C# 提供的用于简化集合遍历的语法,可以说是 C# 为了方便我们这些懒人而发明的,它让咱们不用去操心那些烦人的细节,直接就可以愉快地遍历集合。

它的内部实现依赖于集合对象是否实现了 IEnumerableIEnumerator 接口。

IEnumerable 接口:这个接口就像是个神奇的门把手,轻轻一拧就能打开遍历的大门,里面定义了一个名为 GetEnumerator 的方法,这个方法会返回一个实现了 IEnumerator 接口的对象。

IEnumerator 接口:这个接口有几个重要的成员:

  • MoveNext 方法,用于判断是否还有下一个元素,就像按电梯按钮一样,按一下就能到下一层
  • Current 属性,获取当前元素的值
  • Reset 方法,将迭代器重置到初始位置,实际中很少使用,就像我家里的那台老式收音机,虽然还在,但已经很久没用过了

foreach 循环的基本语法就像这样:

foreach (var item in collection)
{
   
    // 访问 item
}

当使用 foreach 循环时,编译器会生成类似于以下的代码:

IEnumerator<T> enumerator = collection.GetEnumerator();
try 
{
   
    while (enumerator.MoveNext()) 
    {
   
        T item = enumerator.Current;
        // 处理 item
    }
}
finally 
{
   
    IDisposable disposable = enumerator as IDisposable;
    if (disposable != null) 
    {
   
        disposable.Dispose();
    }
}

说明:以上这段代码的关键是 GetEnumerator() 方法返回了一个 IEnumerator 实例,这个实例负责跟踪当前的元素并能够移动到下一个元素。

所以 foreach 循环的内部实现可以简单总结如下

  1. foreach 循环使用迭代器(iterator)来遍历集合,编译器会悄悄生成一个状态机来管理整个过程。
  2. 通过调用 GetEnumerator() 得到的 IEnumerator 对象就像是一个守护者,跟踪当前元素并带你到下一个元素。
  3. MoveNext() 是你前进的钥匙,用于移动到下一个元素,而 Current 用于访问当前元素,告诉你当前看到的是啥。
  4. foreach 循环在处理任何实现了 IEnumerable 接口的集合时,轻松自如。
  5. foreach 循环自动将代码置入 try-finally 块
  6. 若类型实现了 IDisposable 接口,foreach 循环会在循环结束后自动调用 Dispose 方法释放

for 循环的内部实现原理

for 循环是一种传统的循环结构,使用上更为灵活,因为它允许你手动控制循环的开始、结束条件和迭代步长

它的内部实现直接依赖于索引访问或直接迭代,通过索引器来访问每个元素。

for 循环的基本语法如下:

for (int i = 0; i < array.Length; i++)
{
   
    // 访问 array[i]
}

说明:以上这段代码里的 i 是由程序员控制的索引变量,用于访问数组或集合中的元素。

所以对于 for 循环的内部实现,我们可以简单概括如下

  1. for 循环在编译时会被转换为一个简单的计数器循环,直接通过索引访问数组或集合的元素。
  2. 对于数组来说,编译器能够直接计算出元素的地址,因此访问速度非常快。
  3. 在处理 List 或其他集合时,for 依然能顺畅使用索引,但要小心边界检查。

两者区别

经过以上的探索,现在咱们来看看这两哥们儿的区别:

  1. 语法

    • foreach 循环自动管理迭代,你只要负责享受就好。
    • for 循环需要你自己管理索引,要小心别越界!
  2. 可读性

    • foreach 循环语法优雅简洁,像是一首流畅的诗,读起来让人心情愉悦。
    • for 循环的可读性稍逊一筹,尤其是在多维数组里,就像是迷宫一样。
  3. 性能

    • foreach 循环对于实现了 IEnumerable 的集合可能会稍微慢一点。
    • for 循环对于大数组的性能更好些。
  4. 类型安全

    • foreach 循环在编译时会检查元素类型,就像是个严格的门卫。
    • for 循环可能会导致运行时类型转换错误,就像是一本悬疑小说,有时惊险莫测。
  5. 增删集合

    • foreach 不允许你在循环中对集合元素进行增删操作,因为迭代器内部维护了一个对集合版本的控制,任何对集合的增删操作都会使用版本号加1,容易引发异常。
    • for 可以在循环中对集合进行增删操作,因为它直接使用索引器,它不对集合版本号进行判断,所以不存在因为集合的变动而带来的异常(当然,超出索引长度这种情况除外)。

使用场景

  1. foreach 循环的使用场景

    • 程序只需要遍历集合中的每个元素,而不需要增删集合的元素时
    • 集合实现了 IEnumerable 接口(如 List、Array、Dictionary 等)
    • 你希望代码更加简洁和易于理解
  2. 使用 for 循环的场景

    • 程序需要通过索引访问元素,比如在多维数组中
    • 程序需要在循环中增加或删除集合的元素
    • 程序需要在遍历过程中控制循环的步长,比如跳过某些元素

最终建议

  1. 如果你只需要遍历集合,并且无需增删集合元素,多数情况下 foreach 循环是首选
  2. foreach 循环不能取代 for 循环,当你需要访问索引或者需要在循环中增删集合元素时,使用 for 循环
  3. 对于程序健壮性要求比较高的程序,尽量使用 foreach 循环,因为它是类型安全的,并且若类型实现了 IDisposable 接口,它会在循环结束后自动调用 Dispose 方法释放资源
  4. 对于性能敏感的应用程序,比如处理 10 万条数据以上的大数组,可以考虑使用 for 循环
  5. 在编写代码时,尽量保持代码的可读性和简洁性,这样可以减少出错的机会

Happy Coding!🎉

往期精彩

  1. 闲话 .NET(7):.NET Core 能淘汰 .NET FrameWork 吗?
  2. 常用的 4 种 ORM 框架(EF Core,SqlSugar,FreeSql,Dapper)对比总结

我是老杨,一个执着于编程乐趣、至今奋斗在一线的 10年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得。欢迎关注老杨的公众号(名称:代码掌控者),更多干货等你来!

相关文章
|
7月前
|
编译器 数据处理 C#
C#中的异步流:使用IAsyncEnumerable<T>和await foreach实现异步数据迭代
【1月更文挑战第10天】本文介绍了C#中异步流的概念,并通过使用IAsyncEnumerable<T>接口和await foreach语句,详细阐述了如何异步地迭代数据流。异步流为处理大量数据或需要流式处理数据的场景提供了一种高效且非阻塞性的方法,使得开发者能够更优雅地处理并发和数据流问题。
|
2月前
|
Java C#
如何避免在C#循环中使用await
如何避免在C#循环中使用await
128 9
|
6月前
|
开发框架 .NET 程序员
掌握C#语言的精髓:基础知识与实用技能详解(数据类型与变量+ 条件与循环+函数与模块+LINQ+异常+OOP)
掌握C#语言的精髓:基础知识与实用技能详解(数据类型与变量+ 条件与循环+函数与模块+LINQ+异常+OOP)
34 0
|
7月前
|
C#
C# 循环与条件语句详解
使用 switch 语句选择要执行的多个代码块中的一个。 示例:
85 2
|
7月前
|
C#
C#基础语法(判断和循环)
C#基础语法(判断和循环)
62 1
|
7月前
|
C# 图形学
【Unity 3D】C#中while do while for foreach等循环语句的讲解(附测试代码)
【Unity 3D】C#中while do while for foreach等循环语句的讲解(附测试代码)
250 0
C# for和foreach两种循环的效率问题
C# for和foreach两种循环的效率问题
【C#视频】for、while、do-while三种循环
【C#视频】for、while、do-while三种循环
|
1月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
35 3
|
1天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
26 12