C# 语言之循环语句

正常情况下,C#应用程序中,在同一个函数下语句的执行顺序是从上往下依次执行。但是,有的时候我们可能需要实现一些更为复杂的逻辑,比如:重复执行一段代码,也叫做循环执行。

在一般的应用程序中,循环可以表示为如下图的形式:

循环的四要素:

  1. 初始条件:循环最开始的状态。

  2. 循环条件:在什么条件下进行循环,不满足此条件,则循环终止。

  3. 状态改变:改变循环变量值,最终不满足循环条件,从而停止循环。

  4. 循环体:要反复执行的部分。

C# 给我们提供了多种循环语句:forforeachwhiledo...while

for循环

for循环的定义

for循环 是一个允许所编写一个执行特定次数的循环的重复控制结构。通俗点说就是可以重复执行一定次数的循环语句,比如:控制台输出 10 次 “张三”。

  1. for (int i = 0; i < 10; i++)
  2. {
  3. Console.WriteLine($"第{i}个张三");
  4. }
  5. // 输出结果
  6. 0个张三
  7. 1个张三
  8. 2个张三
  9. 3个张三
  10. 4个张三
  11. 5个张三
  12. 6个张三
  13. 7个张三
  14. 8个张三
  15. 9个张三

从上面的示例中,我们可以分析一下循环的四要素。

  • int i = 0对应初始条件:循环从0开始。

  • i < 10对应循环条件:当 i 小于10时,执行循环,当 i 不小于10 循环中止。

  • i++对应状态改变:i++表示每执行一次循环 i 就在当前的基础上 +1

  • Console.WriteLine("张三");对应循环体:也就是我们要循环执行的语句。

for循环的嵌套

多个for循环之间可以相互嵌套,我们先用for循环嵌套输出 5x5 的正方行:

  1. for (int i = 0; i < 5; i++)
  2. {
  3. for (int j = 0; j < 5; j++)
  4. {
  5. Console.Write(" .");
  6. }
  7. Console.Write("\n");
  8. }
  9. // 输出
  10. . . . . .
  11. . . . . .
  12. . . . . .
  13. . . . . .
  14. . . . . .

从上面的例子我们不难看出,循环嵌套会将多个嵌套的for循环都执行一遍才会结束。所以,在实际开发中,尽量避免使用多个for循环嵌套的写法(其它循环同理)。

练习:使用for循环嵌套输出 9x9 乘法表,如下图:

foreach循环

foreach 可以对一个数组或集合对象进行迭代,遍历对象内容。先来看一个简单的示例:

  1. string[] names = { "赵一", "钱二", "孙三", "李四", "周五", "吴六" };
  2. Console.WriteLine("三年级一班有以下学生:");
  3. foreach (var name in names)
  4. {
  5. Console.WriteLine(name);
  6. }
  7. // 输出
  8. 三年级一班有以下学生:
  9. 赵一
  10. 钱二
  11. 孙三
  12. 李四
  13. 周五
  14. 吴六

在上面的示例中,使用foreach遍历输出了names集合中的内容。既然都是循环,那使用for能不能实现呢?当然能。

  1. string[] names = { "赵一", "钱二", "孙三", "李四", "周五", "吴六" };
  2. Console.WriteLine("三年级一班有以下学生:");
  3. for (int i = 0; i < names.Length; i++)
  4. {
  5. Console.WriteLine(names[0]);
  6. }

既然forforeach都能实现循环,那它们之间有什么不同呢?

  • 从编码结构上:for 循环指定了初始值 也就是 i=0,执行了结束条件,也就是 i < names.Length(集合长度),而foreach则省略了这些。

  • 从底层实现上:foreach是通过指针偏移实现的(最初在-1位置,每循环一次,指针就偏移一个单位),而for循环是通过当前索引相对零索引的偏移量(通过首地址和地址偏移量)计算实际访问地址实现的。所以,for 循环可是自定义循环初始索引,而foreach不行。

  • 从使用要求上:使用foreach语句遍历对象要求对象类型实现了枚举接口IEnumerable

  • 从使用效率上:foreach循环访问时会将对象的值复制到栈上,效率比for循环要低。

foreach遍历的实现逻辑

在.NET推出了IEnumerableIEnumerator两个接口之后才有了foreach的用法,使用foreach的前提是被遍历的对象器实现了IEnumerable接口。

实现逻辑:集合或数组实现了IEnumerable接口,并调用GetEnumerator抽象方法返回IEnumerator遍历器,通过使用IEnumerator这个工具来遍历整个类。

  1. IEnumerable<object> obj = new IEnumerable<object>();
  2. IEnumerator tor = obj.GetEnumerator();
  3. while(tor.MoveNext())
  4. {
  5. Console.Write(tor.Current);
  6. }

IEnumerator中定义了一组方法:

  • Current:返回遍历工具所指容器的当前元素。

  • MoveNext:指向下一个元素,当遍历到没有元素时返回一个false

while循环

只要给定条件为 truewhile 循环语句会循环重新执行循环体内的代码。

  1. int i = 1;
  2. while (i > 0)
  3. {
  4. Console.WriteLine("是是是,你说的都对!");
  5. }

上面的例子就是一个简单的while循环语句。但是,如果你执行上面的语句会发现,出现死循环了,一直在输出 “是是是,你说的都对!”。出现死循环的原因:while 语句如果缺少结束语句(将条件变为false)将会一直循环下去。

我们可以将上面的示例加一句代码:

  1. int i = 1;
  2. while (i > 0)
  3. {
  4. Console.WriteLine("是是是,你说的都对!");
  5. i = 0;
  6. }
  7. // 输出
  8. 是是是,你说的都对!

所以,在实际的开发使用while循环时,一定要记得加入结束条件,防止出现死循环。一些特殊情况,比如:定时任务,消费服务等例外。

do…while循环

do...while循环和while循环类似,但是do...while循环最少执行一次循环体,因为do...while循环是在循环的尾部做判断。

  1. int i = 0;
  2. do
  3. {
  4. Console.WriteLine("是是是,你说的都对!");
  5. } while (i > 2);

从上面的例子我们可以看到,在执行完输出操作后,才会去做判断。当判断为 true 时执行下一次循环,判断为 false 跳出循环。do...whilewhile 在使用时一样,也要注意关闭循环,避免引起死循环。