C#入门经典(第7版):C# 6.0 & Visual Studio 2015(.NET开发经典名著)
上QQ阅读APP看书,第一时间看更新

4.3 循环

循环就是重复执行语句。这个技术使用起来非常方便,因为可以对操作重复任意多次(数千次,甚至数百万次),而不必每次都编写相同的代码。

举一个简单例子,下面的代码计算一个银行账户在10年后的金额,假定支付每年的利息,且该账户没有其他款项的存取:

        double balance = 1000;
        double interestRate = 1.05; // 5% interest/year
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;
        balance *= interestRate;

将相同代码编写10次很费时间,如果把10年改为其他值,又会如何?那就必须把该代码行手工复制需要的次数,这是一件多么痛苦的事!幸运的是,完全不必这样做。使用一个循环就可以对指令执行需要的次数。

循环的另一种重要类型是一直循环到给定的条件满足为止。这些循环比上面描述的循环稍简单些(但同样很有用),所以首先从这类循环开始介绍。

4.3.1 do循环

do循环以下述方式执行:执行标记为循环的代码,然后进行一个布尔测试,如果测试结果为true,就再次执行这段代码,并重复这个过程。当测试结果为false时,就退出循环。

do循环的结构如下:

        do
        {
          <code to be looped>
        } while (<Test>);

其中,计算<Test>会得到一个布尔值。

注意:while语句之后必须使用分号。

例如,使用该结构可以把从1到10的数字输出到一列:

        int i = 1;
        do
        {
          WriteLine("{0}", i++);
        } while (i <= 10);

在把i的值写到屏幕上后,使用后缀形式的++运算符递增i的值,所以需要检查一下i <= 10,把10也包含在输出到控制台的数字中。

下面的示例使用这个结构略微修改一下本节前面的代码。该段代码计算一个账户在10年后的余额。这次使用一个循环,根据起始的金额和固定利率,计算该账户的金额需要多少年才能达到某个指定的数额。

试一试:使用do循环:Ch04Ex04\Program.cs

(1)在目录C:\BegVCSharp\Chapter04创建一个新的控制台应用程序Ch04Ex04。

(2)把下述代码添加到Program.cs中:

        static void Main(string[] args)
        {
          double balance, interestRate, targetBalance;
          WriteLine("What is your current balance? ");
          balance = ToDouble(ReadLine());
          WriteLine("What is your current annual interest rate (in %)? ");
          interestRate = 1 + ToDouble(ReadLine()) / 100.0;
          WriteLine("What balance would you like to have? ");
          targetBalance = ToDouble(ReadLine());
          int totalYears = 0;
          do
          {
              balance *= interestRate;
              ++totalYears;
          }
          while (balance < targetBalance);
          WriteLine($"In {totalYears} year{(totalYears == 1 ? "": "s")}
                    you'll have a balance of {balance}.");
          ReadKey();
        }

(3)执行代码,输入一些值,示例结果如图4-4所示。

图4-4

示例说明

这段代码利用固定的利率,对年度计算余额的过程重复必要的次数,直到满足结束条件为止。在每次循环中,递增一个计数器变量,就可以确定需要多少年:

        int totalYears = 0;
        do
        {
          balance *= interestRate;
          ++totalYears;
        }
        while (balance < targetBalance);
        然后就可以将这个计数器变量用作输出结果的一部分:
        WriteLine($"In {totalYears}
                    year{(totalYears == 1 ? "": "s")}
                    you'll have a balance of {balance}.");

注意:这可能是?: (三元)运算符最常见的用法了—— 用最少的代码有条件地格式化文本。这里,如果totalYears不等于1,就在year后面输出一个s。

但这段代码并不完美,考虑一下目标余额少于当前余额的情况,则结果应如图4-5所示。

图4-5

do循环至少执行一次。有时(如这个示例)这并不是很理想。当然,可以添加一条if语句:

        int totalYears = 0;
        if (balance < targetBalance)
        {
          do
          {
              balance *= interestRate;
              ++totalYears;
          }
          while (balance < targetBalance);
        }
        WriteLine($"In {totalYears} year{(totalYears == 1 ? "": "s")} " +
                $"you'll have a balance of {balance}.");

这显然无谓地增加了复杂性。更好的解决方案是使用while循环。

4.3.2 while循环

while循环非常类似于do循环,但有一个明显区别:while循环中的布尔测试在循环开始时进行,而不是最后进行。如果测试结果为false,就不会执行循环。程序会直接跳转到循环之后的代码。

按下述方式指定while循环:

        while (<Test>)
        {
          <code to be looped>
        }

使用方式几乎与do循环完全相同,例如:

        int i = 1;
        while (i <= 10)
        {
          WriteLine($"{i++}");
        }

这段代码的执行结果与前面的do循环相同,它在一列中输出从1到10的数字。下面使用while循环修改上一个示例。

试一试:使用while循环:Ch04Ex05\Program.cs

(1)在目录C:\BegVCSharp\Chapter04中创建一个新的控制台应用程序Ch04Ex05。

(2)修改代码,如下所示(使用Ch04Ex04中的代码作为起点,记住删除原来do循环最后的while语句):

        static void Main(string[] args)
        {
          double balance, interestRate, targetBalance;
          WriteLine("What is your current balance? ");
          balance = ToDouble(ReadLine());
          WriteLine("What is your current annual interest rate (in %)? ");
          interestRate = 1 + ToDouble(ReadLine()) / 100.0;
          WriteLine("What balance would you like to have? ");
          targetBalance = ToDouble(ReadLine());
          int totalYears = 0;
          while (balance < targetBalance)
          {
              balance *= interestRate;
              ++totalYears;
          }
          WriteLine($"In {totalYears} year{(totalYears == 1 ? "": "s")} " +
                  $"you'll have a balance of {balance}.");
              if (totalYears == 0)
                WriteLine(
                  "To be honest, you really didn't need to use this calculator.");
          ReadKey();
        }

(3)再次执行代码,但这次使用少于起始余额的目标余额,如图4-6所示。

图4-6

示例说明

这段代码只是把do循环改为while循环,就解决了上个示例中的问题。把布尔测试移到开头处,就考虑了不需要执行循环的情况,可以直接跳转到输出结果。

当然,这种情况还有一个解决方案。例如,可以检查用户输入,确保目标余额大于起始余额。此时,可以把用户输入部分放在循环中,如下所示:

        WriteLine("What balance would you like to have? ");
        do
        {
          targetBalance = ToDouble(ReadLine());
          if (targetBalance <= balance)
              WriteLine("You must enter an amount greater than " +
                      "your current balance! \nPlease enter another value.");
        }
        while (targetBalance <= balance);

这将拒绝接受无意义的值,得到如图4-7所示的结果。

图4-7

在设计应用程序时,用户输入的有效性检查是一个很重要的主题,本书将列举更多这方面的示例。

4.3.3 for循环

本章介绍的最后一类循环是for循环。这类循环可以执行指定的次数,并维护它自己的计数器。要定义for循环,需要下列信息:

● 初始化计数器变量的一个起始值。

● 继续循环的条件,应涉及计数器变量。

● 在每次循环的最后,对计数器变量执行一个操作。

例如,如果要在循环中,使计数器从1递增到10,递增量为1,则起始值为1,条件是计数器小于或等于10,在每次循环的最后,要执行的操作是给计数器加1。

这些信息必须放在for循环的结构中,如下所示:

        for (<initialization>; <condition>; <operation>)
        {
          <code to loop>
        }

它的工作方式与下述while循环完全相同:

        <initialization>
        while (<condition>)
        {
          <code to loop>
          <operation>
        }

前面使用do和while循环输出了从1到10的数字。下面看看如何使用for循环完成这个任务:

        int i;
        for (i = 1; i <= 10; ++i)
        {
          WriteLine($"{i}");
        }

计数器变量是一个整数i,它的初始值是1,在每次循环的最后递增1。在每次循环过程中,把i的值写到控制台。

注意,当i的值为11时,将执行循环后面的代码。这是因为在i等于10的循环末尾,i会递增为11。这是在测试条件i <= 10之前发生的,此时循环结束。与while循环一样,在第一次执行前,只在条件计算为true时才执行for循环,所以可能根本就不会执行循环中的代码。

最后注意,可将计数器变量声明为for语句的一部分,重新编写上述代码,如下所示:

        for (int i = 1; i <= 10; ++i)
        {
          WriteLine($"{i}");
        }

但如果这么做,就不能在循环外部使用变量i (参见第6章中的“变量的作用域”一节)。

4.3.4 循环的中断

有时需要更精细地控制循环代码的处理。C#为此提供了以下命令:

● break—— 立即终止循环。

● continue——立即终止当前的循环(继续执行下一次循环)。

● return—— 跳出循环及包含该循环的函数(参见第6章)。

break命令可退出循环,继续执行循环后面的第一行代码,例如:

        int i = 1;
        while (i <= 10)
        {
          if (i == 6)
              break;
          WriteLine($"{i++}");
        }

这段代码输出1-5的数字,因为break命令在i的值为6时退出循环。

continue仅终止当前迭代,而不是整个循环,例如:

        int i;
        for (i = 1; i <= 10; i++)
        {
          if ((i % 2) == 0)
              continue;
          WriteLine(i);
        }

在上面的示例中,只要i除以2的余数是0, continue语句就终止当前的迭代,所以只显示数字1、3、5、7和9。

4.3.5 无限循环

在代码编写错误或故意进行设计时,可以定义永不终止的循环,即所谓的无限循环。例如,下面的代码就是无限循环的一个简单例子:

        while (true)
        {
          // code in loop
        }

有时这种代码也是有用的,而且使用break语句或者手工使用Windows任务管理器总是可以退出这样的循环。但当出现这种情形意外时,就会出问题。考虑下面的循环,它与上一节的for循环非常类似:

        int i = 1;
        while (i <= 10)
        {
          if ((i % 2) == 0)
              continue;
          WriteLine($"{i++}");
        }

i是在循环的最后一行代码(即continue语句后的那条语句)执行完后才递增的。如果程序执行到continue语句(此时i为2),程序会用相同的i值进行下一个循环,然后测试这个i值,继续循环,一直这样下去。这就冻结了应用程序。注意仍可以用一般方式退出已冻结的应用程序,所以不必重新启动计算机。