第5章 循环、文件和随机数   主要内容:   5.1 列表框详解 5.6 使用文件存储数据   5.2 while循环 5.7 使用OpenFileDialog和SaveFileDialog控件   5.3 递增和递减运算符   5.4 for循环 5.8 随机数   5.5 do-while循环 5.9 Load事件 5.1 列表框详解   核心概念:列表框控件具有各种方法和属性,程序员可以在代码中使用它们来操纵列表框的内容。   在本书第4章中,介绍了列表框控件。该控件可以显示一个项目列表,并允许用户从列表中选择一个或多个项目。在本章中,将使用列表框控件来显示输出。本章有很多算法来生成数据列表,然后在列表框控件中来显示这些列表。   在第4章中介绍过,列表框项目是通过列表框控件的Items属性来添加的,并且这些项目会显示在列表框中。在设计时,可以使用“属性”窗口将项目添加到控件的Items属性中。程序员也可以编写代码,以便在运行时将项目添加到列表框控件中。要将代码添加到列表框控件中,可以调用该控件的Items.Add方法(Items.Add Method)。以下是该方法的一般格式。 ListBoxName.Items.Add(Item);   ListBoxName是列表框控件的名称。Item是要添加到Items属性的值。例如,在本书学生示例程序的Chap05文件夹中,可以找到一个名为Name List的项目。图5-1显示了该应用程序的窗体。如左侧图像所示,该列表框控件的名字是nameListBox,而按钮控件的名字则是addButton。在运行时,单击addButton控件,则可以看到,有若干项目名称已经添加到右侧图像所示的nameListBox控件中。 图5-1 Name List应用程序   以下是addButton_Click事件处理程序的代码。 1 private void addButton_Click(object sender, EventArgs e) 2 { 3 namesListBox.Items.Add("Chris"); 4 namesListBox.Items.Add("Alicia"); 5 namesListBox.Items.Add("Justin"); 6 namesListBox.Items.Add("Holly"); 7 }   在列表框中也可以添加其他类型的值作为项目。在本书学生示例程序的Chap05文件夹中,可以找到一个名为Number List Box的项目。图5-2显示了该应用程序的窗体。如左侧图像所示,列表框控件的名称是numberListBox,而按钮控件的名称是addButton。在运行时,单击addButton控件,则可以看到,有若干数字项目已经添加到右侧图像所示的nameListBox控件中。 图5-2 Number List应用程序   以下是addButton_Click事件处理程序的代码。 1 private void addButton_Click(object sender, EventArgs e) 2 { 3 namesListBox.Items.Add(10); 4 namesListBox.Items.Add(20); 5 namesListBox.Items.Add(30); 6 namesListBox.Items.Add(40); 7 } 5.1.1 Items.Count属性   列表框控件具有一个Items.Count属性(Items.Count Property),用于报告存储在列表框中的项目的数量。如果列表框为空,则Items.Count属性等于0。例如,假定应用程序有一个名为employeesListBox的列表框控件,如果该列表框中没有项目,则以下if语句将显示一个消息框。 if (employeesListBox.Items.Count == 0) { MessageBox.Show("There are no items in the list!"); }   Items.Count属性保存一个整数值。假设numEmployees是一个int变量,则以下语句可以将employeesListBox中的项目数赋值给numEmployees变量。 numEmployees = employeesListBox.Items.Count; 5.1.2 Items.Clear方法   列表框控件有一个Items.Clear方法(Items.Clear Method),它可以删除Items属性中的所有项目。以下是该方法的一般格式。 ListBoxName.Items.Clear();   例如,假定应用程序具有一个名为employeesListBox的列表框控件,以下语句可以清除列表中的所有项目。 employeesListBox.Items.Clear(); 5.1.3 思考题   5.1 在代码中,如何将一个项目添加到列表框控件?   5.2 如何确定存储在列表框控件中的项目数量?   5.3 如何清除列表框控件的内容? 5.2 while循环   核心概念:只要布尔表达式为true,while循环就将导致一个或一组语句重复。   while的字面意思是“在……期间”或“与……同时”,顾名思义,while循环的工作方式就是:在一个布尔表达式为true期间,执行一些任务。该循环有以下两个部分。   (1)一个布尔表达式,测试该表达式可以获得一个true或false值;   (2)一个语句或一组语句,只要布尔表达式为true就重复执行。   图5-3显示了while循环的逻辑。 图5-3 while循环的逻辑   菱形符号代表被测试的布尔表达式。在图5-3中可以看到,如果该表达式的值为true,则执行一个或多个语句,之后程序的执行流回到菱形符号正上方的点。布尔表达式再次测试,如果仍然为true,则重复该过程;如果布尔表达式为false,则程序退出循环。每次循环执行它的语句时,以术语来说,就是循环正在迭代,或者说是执行迭代(Iteration)。   以下是while循环的通用格式。 while(BooleanExpression) { 语句; 语句; 更多语句…… }在布尔表达式为true时, 该组语句将重复执行  在该通用格式中,第一行称为while子句(while Clause)。while子句以单词while开头,后面跟着一个用括号括起来的布尔表达式。从下一行开始,是用大括号括起来的一组语句,这个语句块被称为循环体(Body)。   当while循环执行时,测试布尔表达式。如果布尔表达式为true,则执行出现在循环体中的语句,然后循环再次开始。如果布尔表达式为false,则循环结束,程序将转到循环之后的语句继续执行。   可以说,循环体中的语句是有条件执行的,因为它们只在布尔表达式为true的条件下执行。如果要编写的while循环只有一个执行语句,则不必把该语句放在大括号里面。像这样的循环可以写成如下通用格式。 while (BooleanExpression) 语句;   当以这种格式编写的while循环执行时,首先测试布尔表达式。如果其值为true,则执行下一行出现的一条语句,然后重新开始循环。如果布尔表达式的值为false,则循环结束。   虽然在循环体中只有一条语句时,不需要大括号,但如能使用它们则更为恰当。其通用格式如下。 while(BooleanExpression) { 语句; }   本书第4章在讨论各种if语句时曾经提到过,这是一种很好的编程风格,因为它减少了错误发生的几率。如果循环体中有多个语句,那么这些语句必须用大括号括起来。如果程序员习惯于把条件执行语句放在一对大括号中,那么就不太可能忘记它们。   另外还应该注意的是,循环体中的语句是缩进的。和if语句一样,该缩进样式使得代码更容易阅读和调试。通过缩进循环体中的语句,可以直观地将它们和周围的代码区别 开来。   现在来看一个例子。在本书学生示例程序的Chap05文件夹中,可以找到一个名为while Loop Demo的项目。图5-4显示了该应用程序的窗体。如左侧图像所示,Button控件的名称是goButton。在运行时,单击goButton控件,将出现如右侧图像所示的消息框。当单击“确定”按钮关闭消息框时,会显示另一个相同的消息框。消息框总共显示5次。 图5-4 while Loop Demo项目的窗体   以下是goButton_Click事件处理程序的代码。 1 private void goButton_Click(object sender, EventArgs e) 2 { 3 //声明一个变量count来计算循环迭代 4 int count = 1; 5 6 //在消息框中显示"Hello"字符串5次 7 while (count <= 5) 8 { 9 //显示消息框 10 MessageBox.Show("Hello"); 11 12 //给count变量加1 13 count = count + 1; 14 } 15 }   现在来仔细看一看这个代码。在第4行中声明了一个名为count的int变量,并将其初始化为值1。while循环从第7行开始,可以看到while循环的测试表达式是count <= 5。只要count变量小于或等于5,则while循环体中的语句将不断重复。在循环体内部,第10行将在消息框中显示“Hello”字符串,然后第13行会给count变量加1。这是循环体中的最后一个语句,所以执行之后,循环重新开始。它再次测试布尔表达式,如果仍然为true,则执行循环体中的语句。该循环如此重复,直到布尔表达式count <= 5为false,如图5-5所示。图5-6显示了该循环的流程图。 图5-5 while循环 图5-6 while循环的流程图 5.2.1 while循环是一个预测试循环   while循环被称为预测试循环(Pretest Loop),这意味着它会在执行迭代之前测试它的条件。由于测试是在循环开始时完成的,通常必须在循环之前执行一些步骤,以确保循环至少执行一次。注意while循环演示程序中count变量的声明如下。 int count = 1;   count变量的初始值为1。如果count已经被初始化为一个大于5的值,如下面的程序所示,则该循环永远不会执行。 1 private void goButton_Click(object sender, EventArgs e) 2 { 3 //声明变量count以计算循环迭代 4 int count = 6; 5 6 //该循环永远不会迭代 7 while (count <= 5) 8 { 9 //显示消息框 10 MessageBox.Show("Hello"); 11 12 //给count变量+1 13 count = count + 1; 14 } 15 }   while循环的一个重要特性是,如果布尔表达式的值为false,则循环将永远不会迭代。如果要确保while循环至少执行一次,则在初始化相关数据时,必须要注意使布尔表达式刚开始的值为true。 5.2.2 计数器变量   在while循环演示应用程序中,变量count初始值为1,然后在每次循环迭代期间将1添加到变量count中。只要count小于或等于5,循环就会执行。变量count被用作计数器变量(Counter Variable),这意味着它在每次循环迭代中有规律地增加。本质上,count变量保持着循环执行的迭代次数。计数器变量通常用于控制循环迭代的次数。   教程5-1提供了编写循环和使用计数器变量的练习。在本教程中,将编写一个while循环,计算一个银行账户在若干个月内赚取的利息金额。 教程5-1:使用循环计算账户余额   在本教程中,将完成Ending Balance应用程序。该项目已经完成了一部分,位于本书学生示例程序的Chap05文件夹中。该应用程序的窗体如图5-7所示。 图5-7 Ending Balance窗体及其控件名   在完成该应用程序时,它将允许用户将账户的初始余额输入到startingBalTextBox控件中,将要计息的月份数输入到monthsTextBox控件中。当用户单击calculateButton控件时,应用程序会计算账户的到期余额。账户的每月利率是0.005,利息是按月计算的。   步骤1:启动Visual Studio。在本书的学生示例程序的Chap05文件夹中打开名为Ending Balance的项目。   步骤2:在设计器中打开Form1窗体。图5-7显示了该窗体以及重要控件的名称。   步骤3:现在将为按钮控件创建Click事件处理程序。在本教程的末尾,程序5-1显示了窗体的完整代码。在编写事件处理程序时,可以参考程序5-1。   在设计器中双击calculateButton按钮控件,打开代码编辑器,此时可以看到一个名为calculateButton_Click的空事件处理程序。通过输入程序5-1中第22~59行显示的代码来完成calculateButton_Click事件处理程序。现在来仔细看一看这些代码。   第23行:该语句声明了一个名为INTEREST_RATE的decimal类型常量,其值设置为0.005,这是每月的利率。   第26~28行:这些语句声明了以下变量。 * balance,一个decimal类型变量,用来保存账户余额。 * months,一个int变量,用来保存账户计算利息的月份。 * count,一个int值,用于计算循环迭代的月份。注意,count变量是用值1初始 化的。   第31行:这个if语句试图将startingBalTextBox.Text转换为decimal类型的数值。如果转换成功,则将结果存储在balance变量中,程序继续在第33行执行;如果转换不成功,则程序跳转到第55行的else子句,第58行显示错误消息,告诉用户:输入的初始余额的值无效(“Invalid value for starting balance”)。   第34行:这个if语句试图将monthsTextBox.Text转换为一个int类型的值。如果转换成功,则将结果存储在months变量中,程序继续在第36行执行;如果转换不成功,则程序跳转到第49行的else子句,第52行显示错误消息,告诉用户:输入的计息月份数值无效(“Invalid value for months”)。   第37行:这是一个while循环的开头。只要表达式count <= months为true,循环就会执行。   第39~43行:这些语句是while循环的主体。第40行计算每月的利息,并将其添加到balance变量。43行在count变量中加1。   第47行:该语句在循环完成所有迭代之后执行。它将balance变量的值转换为字符串(采用货币格式),并将结果字符串赋值给endingBalanceLabel控件的Text属性。   步骤4:将视图切换回设计器,然后双击clearButton控件。在代码编辑器中,可以看到一个名为clearButton_Click的空事件处理程序。通过输入程序5-1中第64~70行显示的代码来完成clearButton_Click事件处理程序。   步骤5:将视图切换回设计器,然后双击exitButton控件。在代码编辑器中,可以看到一个名为exitButton_Click的空事件处理程序。通过输入程序5-1中第75~76行显示的代码来完成exitButton_Click事件处理程序。   步骤6:保存项目。然后按F5键或单击工具栏上的“启动”按钮(),编译并运行该应用程序。   首先输入1000作为初始余额,输入48作为计息月份数。单击Calculate按钮,此时在endingBalanceLabel标签中将显示到期余额为1?270.49。以本测试输入的月数为例,第36行的while循环迭代了多少次?答案是:48次。   接下来,单击Clear按钮清除所有文本框和到期余额。现在,输入100作为初始余额,1作为计息月份。单击Calculate按钮,此时在endingBalanceLabel标签中将显示到期余额为100.50。这一次while循环迭代次数是多少呢?答案:1次。   继续根据需要测试应用程序。完成后,单击Exit按钮,窗体应该关闭。如果打算继续下一个教程,请在Visual Studio中保持此项目的打开状态。   程序5-1 Ending Balance应用程序Form1窗体的完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Ending_Balance { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void calculateButton_Click(object sender, EventArgs e) { //Constant for the monthly interest rate. const decimal INTEREST_RATE = 0.005m; //Local variables decimal balance; //The account balance int months; //The number of months int count = 1; //Loop counter, initialized with 1 //Get the starting balance. if (decimal.TryParse(startingBalTextBox.Text, out balance)) { //Get the number of months. if (int.TryParse(monthsTextBox.Text, out months)) { //The following loop calculates the ending balance. while (count <= months) { //Add this month's interest to the balance. balance = balance + (INTEREST_RATE * balance); //Add one to the loop counter. count = count + 1; } //Display the ending balance. endingBalanceLabel.Text = balance.ToString("c"); } else { //Invalid number of months was entered. MessageBox.Show("Invalid value for months."); } } else { //Invalid starting balance was entered. MessageBox.Show("Invalid value for starting balance."); } } private void clearButton_Click(object sender, EventArgs e) { //Clear the TextBoxes and the endingBalanceLabel control. startingBalTextBox.Text = ""; monthsTextBox.Text = ""; endingBalanceLabel.Text = ""; //Reset the focus. startingBalTextBox.Focus(); } private void exitButton_Click(object sender, EventArgs e) { //Close the form. this.Close(); } } }教程5-2:改进Ending Balance应用程序   本教程将改进在教程5-1中创建的Ending Balance应用程序。首先,将一个列表框控件添加到应用程序的窗体中,如图5-8所示。然后修改calculateButton_Click事件处理程序,以便在列表框中显示每月的结束余额。图5-9显示了当用户输入初始余额为1000和月份为8时,窗体显示的结果。 图5-8 修改后的Ending Balance应用程序