1.4 实验指导
本章安排5个各有侧重的实验。
(1)了解VC#集成开发环境以及C#程序设计的一般步骤。
(2)通过VC#的“控制台应用程序”来编写并运行简单的C#程序。
(3)通过VC#的“Windows窗体应用程序”来编写并运行简单的C#程序。
(4)通过使用了特殊控件(日历)的“Windows窗体应用程序”来编写并运行C#程序。
(5)了解VC#集成开发环境中程序调试的一些常用方法。
通过本章实验,基本掌握通过VC#软件中两种不同形式的项目(控制台应用程序、Windows窗体应用程序)来编写并处理C#程序的一般方法,初步理解程序设计的概念、C#程序的特点以及C#程序设计的一般方法。
实验1-1 C#程序的编辑、编译和运行
本实验中,需要创建一个VC#的控制台工程,通过它来编写并运行一个简单的C#程序,其功能为显示一个字符串。
1. 启动VC#
在Windows开始菜单中,单击“所有程序”并选择其中的Visual Studio 2013(或其他版本)菜单项,打开Visual Studio窗口。
2. 创建VC#控制台工程
(1)选择菜单项:“文件”→“新建”→“新建项目”对话框。
(2)在左侧的模板中,展开“Visual C#节点”,然后在右侧选择“控制台应用程序”。
(3)在“名称”文本框中输入“程序1”作为当前项目的名称;在“位置”文本框中输入一个文件夹的路径名,指定当前项目的保存位置(可通过“浏览”按钮选择文件夹)。
(4)单击“确定”按钮,关闭“新建项目”对话框。
这时,VC#自动创建一个“控制台程序”,并自动进入C#代码的编辑状态。应注意观察这种情况下的VC#主窗口,尤其要关注其中的资源管理器和代码编辑器的内容。
3. 键入并编辑C#代码
在刚打开的Program.cs文件编辑窗口中键入一些代码,使其包含以下内容。
//实验1-1_输入一个字符串 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace程序1 { class Program { static void Main(string[] args) { //输出一个字符串 Console.WriteLine("第{0}个程序", 1); } } }
4. 运行C#程序
(1)选择菜单项:“生成”→“生成程序1”,编译并运行当前程序。
(2)选择菜单项:“调试”→“开始执行(不调试)”,再次运行当前程序。
运行程序时,也可单击工具栏上的按钮,自动连续地完成编译和运行工作。
程序运行后,如果不出现错误,则将弹出运行窗口并在其中显示指定的字符串。注意观察运行窗口,并对照自己添加的输出字符串的语句,体会该语句的功能。
5. 保存C#程序
选择菜单项:“文件”→“全部保存”,或者单击工具栏上的“保存”按钮,将当前控制台工程中的所有文件全部保存在创建该工程时指定的文件夹中。
6. 修改并运行C#程序
(1)在Main方法中修改并添加代码,使其包含以下内容。
static void Main(string[] args) { Int x=Convert.ToInt16(Console.ReadLine()); Int y = 2 * x + 1; Console.WriteLine("y={0}", y); }
(2)运行该程序,观察运行结果。
7. 再次修改、运行并保存C#程序
(1)在Main方法中,修改并添加代码,使其包含以下内容。
static void Main(string[] args) { int x, y; x=Convert.ToI nt16(Console.ReadLine()); if (x >= 0) y = 2 * x + 1; else y = -x; Console.WriteLine("y={0}", y); }
(2)运行该程序,观察运行结果。
(3)选择菜单项:“文件”→“全部保存”,将当前控制台工程中的所有文件全部保存在创建该工程时指定的文件夹中。
实验1-2 简单控制台应用程序
本实验中,通过VC#的控制台应用程序来编写并运行两个不同种类的C#程序。
• 通过多个字符或者字符串的输出拼凑一个图案。
• 求分段函数的值。
1. 按指定格式显示字符串
【程序的功能】
显示图1-22所示的图案。
【算法分析】
本程序中,只需将所要输出的图案中的每一行看作一个字符串,然后多次输出一个一个的字符串,即可拼凑成这个图案。例如,语句
图1-22 显示图案程序的运行结果
Console.WriteLine("*****************************");
可用于输出第1行;语句
Console.WriteLine(" Ji ShangHe");
可用于输出第2行。按照这种方式,不难编写出整个程序。
编写程序时,需要注意两个问题:一是数准每行中空格的位置和数目,二是充分利用VC#中的字符串复制功能来编写输出相同内容的行的语句。
【程序设计步骤】
(1)创建一个名为“显示字符图案”的VC#控制台应用程序。
要求:在桌面或U盘上创建相应的项目文件夹(不使用默认的路径)。
(2)编写C#源程序代码。
请先补全①、②、③、④和⑤处的C#代码,然后在编辑窗口中输入这个程序。本书中很多实验内容都将这样给出。
//实验1-2-1_ 显示字符图案 … static void Main(string[] args) { Console.WriteLine("*****************************"); Console.WriteLine(" Ji ShangHe"); ① ② ③ ④ ⑤ }
(3)编译、链接并运行C#程序。
(4)保存C#程序。
如果该程序在编译连接时发现错误或者运行后未输出预期结果,则需要找出程序中的问题,修改所发现的错误,并在改正后再次编译、连接和运行程序。
【程序的改进】
本程序中,也可采用字符串变量来存放需要显示的字符串,并在此后的输出语句中显示出来。例如,语句
string s1="*****************************";
定义了字符串变量s1并将字符串"*****************************"赋值给它。此后便可使用
Console.WriteLine(s1);
语句输出这个字符串而且可以多次输出。
考虑到初学者的实际困难,下面给出采用这种方式编写的C#程序。
static void Main(string[] args) { string s1="******* **********************"; string s2=" Ji ShangHe"; string s3=" No.28 West Xianning Road"; string s4=" Xi'an China,710049"; string s5=" Tel.86-29-82668888"; string s6=" Emai.shJi1960@183.com.cn"; Console.WriteLine(s1); Console.WriteLine(s2); Console.WriteLine(s3); Console.WriteLine(s4); Console.WriteLine(s5); Console.WriteLine(s6); Console.WriteLine(s1); }
2. 求分段函数的值
【程序的功能】
根据下面的函数,由已知的x值计算y值。
【算法分析】
参照1-1-1小节。
【程序设计步骤】
(1)创建一个名为“分段函数”的VC#控制台应用程序。
要求:在桌面或U盘上创建相应的项目文件夹(不使用默认的路径)。
(2)编写C#源程序代码。
在Main方法中添加代码,使其包含以下内容。
static void Main(string[] args) { int x, y; x=Convert.ToInt16(Console.ReadLine()); if (x > 0) y = 2 * x + 1; else if (x=0) y = -1; else y =-x+3; Console.WriteLine("y={0}", y); }
(3)编译、连接并运行C#程序。
(4)保存C#程序。
实验1-3 简单Windows窗体应用程序
在本实验中,先创建一个简单的Windows窗体应用程序,再修改它,使之能计算学生的考试成绩并显示出来。
1. 简单的Windows窗体应用程序
【程序的功能】
单击按钮后,将文本框中的内容输出到列表框中。
【程序设计步骤】
(1)创建一个名为“窗体程序1”的控制台应用程序。
(2)在VC#集成开发环境的主窗口中,右键单击右侧的“工具箱”标签并选择“停靠”菜单项,使VC#窗口成为图1-23(a)所示的状态。
(3)设计图1-23(b)所示的窗体,其中包括一个标签、一个文本框、一个按钮和一个列表框,除将标签的Text属性改为“姓名”外,其余属性一律为默认值(自动赋值)。
(4)双击按钮button1,在代码编辑器中自动产生的button1的单击事件方法中添加代码,使其包含以下内容。
private void button1_Click(object sender, EventArgs e) { string s = textBox1.Text; listBox1.Items.Add(s); }
这时,代码编辑器的内容如图1-23(c)所示。
图1-23 设计“窗体程序1”的VC#环境
(5)运行程序。然后在文本框中输入姓名,单击按钮使其显示在列表框中。
【修改程序】
按以下步骤修改程序,使其能够按成绩=考试成绩*0.7+平时成绩*0.3计算学生的成绩,并显示在列表框中。
(1)修改窗体,添加两个标签、两个文本框,使其成为图1-24(a)所示的样式。
(2)双击按钮button1,在代码编辑器中自动产生的button1的单击事件方法中添加代码,使其包含以下内容。
private void button1_Click(object sender, EventArgs e) { string s = textBox1.Text+" 成绩:"; float g1 = float.Parse(textBox2.Text); float g2 = float.Parse(textBox3.Text); float g = g1* 0.7f + g2 * 0.3f; s += Math.Round(g); listBox1.Items.Add(s); }
图1-24 设计“窗体程序1”的VC#环境
• 字符串变量s用于形成列表框中显示的一行,其初值为用户在文本框TextBox1中输入的学生姓名后跟“成绩:”字样。
• 浮点(float)型变量g1、g2和g分别表示学生的考试成绩、平时成绩和总成绩。因为C#在计算含有浮点数的表达式时都是自动按双精度(double)型处理的,故数字0.7和0.3必须明确标记为0.7f和0.3f才能与两个float型变量g1和g2一起运算。
• 为了将成绩四舍五入成整型数,使用了Math类的Round方法。
(3)运行程序,然后分别在3个文本框中输入姓名、考试成绩和平时成绩,单击按钮计算总成绩并使其显示在列表框中。
【保存程序】
选择“文件”菜单的“全部保存”项,或者按组合键Ctrl+Shift+S,保存该项目中的所有文件。
注意
创建应用程序时,可先在“新建项目”对话框中选择保存位置(如桌面或E盘根目录),然后右键单击当前位置空白处,选择“新建”→“文件夹”命令,创建一个文件夹,然后以当前项目的名字来命名它。此后,该项目中的所有文件都会保存在这个文件夹中了。
实验1-4 有特殊控件的Windows窗体应用程序
【程序的功能】
通过Windows窗体应用程序来实现用户输入的日期与日历表上的日期互换。也就是说,在图1-25所示的窗口(程序运行后显示的与用户交互的窗体)上实现以下功能。
• 用户分别在3个文本框中输入年、月、日,并单击按钮,该日期在日历表上显示出来。
• 用户在日历表上选择一个日期,该日期在3个文本框中显示出来。
图1-25 “日期转换程序”的运行窗口
【程序设计步骤】
(1)创建一个名为“窗体程序2”的控制台应用程序。
(2)在VC#集成开发环境的主窗口中,右键单击右侧的“工具箱”标签并选择“停靠”菜单项,使VC#窗口成为图1-23(a)所示的状态。
(3)比照图1-25所示的设计窗体,其中包括以下内容。
• 一个日历控件MonthCalendar,用于显示日历。
• 3个Label,其Text属性分别取值“年”“月”和“日”。
• 3个TextBox,分别用于输入或显示“年”“月”“日”。
• 两个按钮,其Text属性分别取值“<<”和“>>”,其单击事件方法中分别用于添加实现将日历上选定的日期转换到文本框或者将文本框上输入的日期转换到日历上。
(4)双击按钮button1,在代码编辑器中自动产生的button1的单击事件方法中添加代码,使其包含以下内容。
private void button1_Click(object sender, EventArgs e) { //用户输入的年月日转换(选定)到日历控件上 int year = int.Parse(textBox1.Text); int month = int.Parse(textBox2.Text); int day = int.Parse(textBox3.Text); DateTime dt = new DateTime(year, month, day); monthCalendar1.SetDate(dt); }
(5)双击按钮button2,在代码编辑器中自动产生的button2的单击事件方法中添加代码,使其包含以下内容。
private void button2_Click(object sender, EventArgs e) { //日历控件上选定的年月日转换(输出)到文本框上 textBox1.Text = monthCalendar1.SelectionStart.Year.ToString(); textBox2.Text = monthCalendar1.SelectionStart.Month.ToString(); textBox3.Text = monthCalendar1.SelectionStart.Day.ToString(); }
(6)运行该程序,显示出窗口。
在文本框中输入年、月、日,然后单击按钮,观察日历控件的变化。
在日历控件上选择一个日期,然后单击按钮,观察3个文本框的变化。
(7)保存(完全保存)C#程序。
实验1-5 程序的调试和运行
在编译、连接阶段,输出窗口将会显示当前编译的信息。如果遇到错误,还能够显示出错误的位置(第几行)和性质(什么错误)。此时,只需双击错误提示行即可定位到程序中出错的地方,如果遇到的是漏写了一个分号之类的小错误,就可以立即改正它。但错误的起因往往来自于其他行中的错误而非本行有什么问题,这就需要仔细察看相关的行了。如果对显示出来的错误性质不太理解,可以加亮这个错误提示行,然后按F1键调出并查对该错误的解释。
如果改正了程序中所有的错误,使得编译通过了,但结果仍不正确,就需要使用调试的办法了。调试方法主要有两种:一是设置断点进行单步调试,二是运行时调试,即在程序执行过程中进行测试。
本实验中,将利用编译、链接与运行时的出错信息并设置断点来调试一个简单的程序。
假设要编程序实现以下功能。
(1)令x=15,y=18。
(2)计算s=x+y,d=x-y,q=x/y(整除),r=x%y(求余数)。
(3)计算res=s+2d+3q+4r。
(4)输出res。
初步给出下面的程序。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 实验1_3 { class Program { static void Main(string[] args) { int aa, bb; int s, d, q, r, cc; double cc; console.WriteLine("aa=? "); aa=Console.ReadLine(); s = aa + bb; d = s - bb; q = aa / bb; r = aa % bb; cc = s + 2 * d + 3 * q + 4 * r; Console.WriteLine("cc={0}", cc); } } }
1. 利用编译、链接时的出错信息改正错误
编译、连接阶段的常见错误包括以下几个。
(1)语法错误:可以检查是否存在下列问题。
• 是否缺少了分号(行结束符)。
• if语句(分支语句)中的if子句和else子句是否匹配。
• switch语句(多分支语句)的用法正确与否等。
(2)变量、函数未定义或者重定义:可以检查是否存在下列问题。
• 变量大小写。
• 是否引用了相应的命名空间,有时候,可能还引用用户自定义的命名空间。
(3)连接错误:如果程序中使用了动态链接库(DLL),无论是自己制作的还是Windows本身已有的,都可能会出现这种问题。此时,可以查看究竟是哪个方法(函数)出错了。例如,如果调用了一个Windows API函数(微软为方便Windows开发人员调用系统底层功能而公开的一系列函数接口。.NET中很多函数就是系统底层API的封装),而在MSDN的相应内容中明确地指出该函数需要引用某个命名空间,就一定要用using语句引用了它之后才能使用。
注意
动态链接库DLL(Dynamic Link Library)是一个包含可由多个程序同时使用的代码和数据的库。微软公司在Windows操作系统中用这种方式来实现函数库的共享。这些库函数的扩展名是.DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统驱动程序)。
本例中,将初步给出的程序输入并发出运行命令之后,VC#弹出图1-26(a)所示的错误提示消息框,同时在错误列表窗口中显示相应的错误信息,如图1-26(b)所示。
图1-26 代码编辑器、错误列表窗口及错误提示消息框
可以看出,程序没有通过编译并且显示了4个错误信息。可按以下步骤逐个查找并改正这些错误。
(1)单击第1个错误列表行,光标自动定位到第18行,根据提示信息
使用了未赋值的局部变量"bb"
很容易查到出错的原因是表达式中用到的变量“bb”未在前面赋值。可在之前添加语句
bb=int.Parse(Console.ReadLine());
(2)单击第1个错误列表行,光标自动定位到第15行,根据提示信息
已在此范围定义了名为"cc"的局部变量
很容易查到出错的原因是两个语句中都定义了“cc”变量。考虑到本程序要求进行整数运算,运算结果也是整数,因而删除定义双精度(浮点)型变量cc的语句即可。
(3)单击第2个错误列表行,光标自动定位到第16行,根据提示信息
当前上下文不存在名称"console"
可以查到出错的原因是“console”关键字的首字母没有大写(C#代码中区分大小写),改成大写字母即可。
(4)单击第2个错误列表行,光标自动定位到第17行,根据提示信息
无法将类型"string"隐式转换为"int"
可以查到出错的原因是未将由Console.ReadLine()输入的数字型字符串转换为整数,并错误地赋值给了左式的整型变量aa(数据类型不匹配),可以改成
aa=int.Parse.(Console.ReadLine());
2. 利用运行时错误提示信息改正错误或改进程序
改正了上述4个编译连接过程中的错误之后,本程序在运行时会暂停,等待用户输入bb的值之后重新运行,如果不小心输入了0值,便会出现运行错误而暂停了。这时,运行窗口如图1-27(a)所示,其中有以下错误提示信息。
输入字符串的格式不正确
同时,C#还会弹出图1-27(b)所示的消息框。
图1-27 输入了初步给出的程序之后的用户界面
阅读程序便可发现:变量bb要在后面的表达式中作为除数,故其值不能为零。为了防止这种错误出现,可在程序前面加上如下语句。
Console.WriteLine("bb(不能等于0)= ? ");
从而在用户输入变量bb的值之前给以提示。
3. 设置断点查找逻辑错误
经过上面的调试之后,程序成为下面的样子。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 实验1_5 { class Program { static void Main(string[] args) { int aa, bb; int s, d, q, r, cc; Console.WriteLine("aa = ? "); aa=int.Parse(Console.ReadLine()); Console.WriteLine("bb (不能等于 0)= ? "); bb=int.Parse(Console.ReadLine()); s = aa + bb; d = s - bb; q = aa / bb; r = aa % bb; cc = s + 2 * d + 3 * q + 4 * r; Console.WriteLine("cc={0}", cc); } } }
这时,编译、连接和运行时都不再出问题。再次运行后,结果如图1-28所示。
图1-28 改正后程序的运行结果
但这个结果却是错的:变量cc的值应该是87而不是123。也就是说,虽然这个程序通过了所有语法检查,但逻辑上或者算法上仍然有错误。
本例中,按以下步骤查找并改正错误。
(1)右键单击计算cc变量的语句行,选择菜单项:“断点”→“插入断点”,在该行插入断点。
(2)再次运行程序,运行到断点处时自动暂停。
(3)将鼠标逐个移过计算cc所涉及的每个变量,查看变量的值。当悬停到d变量上时,显示其值为15,初步判定有误,看代码可知右式中的aa误写为s了,如图1-29所示。
图1-29 设置了断点的编辑窗口
改正这个错误后,程序成为:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 实验1_5 { class Program { static void Main(string[] args) { int aa, bb; int s, d, q, r, cc; //double cc; Console.WriteLine("aa = ? "); aa=int.Parse(Console.ReadLine()); Console.WriteLine("bb (不能等于 0)= ? "); bb=int.Parse(Console.ReadLine()); s = aa + bb; d = aa - bb; q = aa / bb; r = aa % bb; cc = s + 2 * d + 3 * q + 4 * r; Console.WriteLine("cc={0}", cc); } } }
再次运行后,输出的结果如图1-30所示。
图1-30 修改后程序的运行窗口
[1]见1.1.2节。
[2]为提高程序质量(可靠性)而采用的较为规范的程序设计方法。