640 likes | 786 Views
Visual C#.NET 程序设计. 第 8 章 维护数据 ( 二 ). 学习和实践的目标. 需求分析要求:增加、删除、修改与学生成绩相关的基础数据,即学生、学年、课程、教师、成绩比例和操作员。 本章将充分利用对象的继承性和多态性完成所有基础数据的增加、修改和删除。. 实现目标的知识点. 数组 跳转和循环控制 数据类型转换. 8.1 实现目标的步骤. 利用对象的继承性和多态性维护数据的步骤如图 8-1 所示。. 8.2 数组与循环控制处理.
E N D
Visual C#.NET程序设计 第8章 维护数据(二)
学习和实践的目标 • 需求分析要求:增加、删除、修改与学生成绩相关的基础数据,即学生、学年、课程、教师、成绩比例和操作员。 • 本章将充分利用对象的继承性和多态性完成所有基础数据的增加、修改和删除。
实现目标的知识点 • 数组 • 跳转和循环控制 • 数据类型转换
8.1 实现目标的步骤 • 利用对象的继承性和多态性维护数据的步骤如图8-1所示。
8.2 数组与循环控制处理 • 数据及其处理总是贯穿在应用程序的整个开发过程中。相关的数据可以聚合成集。通过数据集的集中处理,可以极大地提高数据处理的灵活性和效率。 8.2.1 数组 • 以System.Array为基类型的数组是一种包含若干元素的数据结构,是特殊的数据集合。 • 每个数组中包含的若干个元素都具有相同的数据类型,可通过一个统一的数组变量名和下标(索引)来唯一地确定数组中的元素以进行访问。 • C#中的数组有一维数组、多维数组和交错数组。
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • 一维数组的声明格式如下: • 数组类型[] 数组变量; • 数组类型可以为.NET平台下支持的任意数据类型,如:Int16、 Int32和Int64等,它们都处于System命名空间中。假设事先已引入System命名空间。 • 如: • Int32[] a;
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • 数组类型属于引用类型,因此数组变量的声明只是为数组实例的引用留出空间,实际的数组实例在运行时使用new 运算符动态创建。如: • a=new Int32[10]; • 以上语句才会使数组a成为实际上的、物理上存在的整型数组,数组a将会包含10个Int32型的元素。
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • new 运算符从真正意义上创建了数组,同时也指定了新数组实例的长度 (length),它在该实例的生存期内是固定不变的。 • 数组元素的索引范围从 0 到 Length - 1。 • new 运算符自动将数组的元素初始化为它们的默认值,例如将所有数值类型初始化为零,将所有引用类型初始化为 null。
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • 下面的控制台应用程序创建并初始化了一个 Int32 类型的一维数组,同时还在控制台上输出该数组中的内容: • using System; • class Test • { • static void Main() • { • Int32[] a = new Int32[10]; • for (Int32 i = 0; i < a.Length; i++) a[i] = i * i;
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • for (Int32 i = 0; i < a.Length; i++) • { • Console.Write ("a["+i+"] = "+a[i]+””); • } • } • } • 所生成的输出如下: • a[0]=0 a[1]=1 a[2]=4 a[3]=9 a[4]=16 a[5]=25 a[6]=36 a[7]=49 a[8]=64 a[9]=81
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • new 运算符还允许使用数组初始值设定项,以指定数组元素的初始值。数组初始值设定项是在一个位于定界符 {} 之间的表达式列表。下面的示例分配并初始化具有三个元素的 Int32[]: • Int32[] a = new Int32[] {1, 2, 3};
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • 注意数组的长度是从 {} 之间的表达式个数推断出来的。对于局部变量和字段声明,可以进一步简写,不必再次声明数组类型。例如: • Int32[] a = {1, 2, 3}; • 前面的两个示例都等效于下面的示例: • Int32[] a = new Int32[3]; • a[0] = 1; • a[1] = 2; • a[2] = 3;
8.2 数组与循环控制处理 8.2.1 数组 1.一维数组 • 当一个数组创建表达式既包含显式维度长度,又包含数组初始值设定项时,长度必须是常量表达式,同时各嵌套级别的元素数目必须与相应的维度长度相匹配。参见以下示例: • Int32 i = 3; • Int32[] x = new Int32[3] {0, 1, 2}; // 正确 • Int32[] y = new Int32[i] {0, 1, 2}; // 错误, i 不是常量 • Int32[] z = new Int32[3] {0, 1, 2, 3}; // 错误, 维度长度和一个数组初始值设定项不匹配
8.2 数组与循环控制处理 8.2.1 数组 2.多维数组 • 数组类型的维数也称为数组类型的秩 (rank),它是数组类型方括号之间的逗号个数加上 1。例如: • Int32[,]a1 = new Int32[10, 5]; //分配一个二维数组 • Int32[,,]a2 = new Int32[10, 5, 2]; //分配一个三维数组 • a1 数组包含 50 (10 × 5) 个元素, a2 数组包含 100 (10 × 5 × 2) 个元素。 • 多维数组的初始值设定项必须具有与数组维数同样多的嵌套级别。最外面的嵌套级别对应于最左边的维度,而最里面的嵌套级别对应于最右边的维度。
8.2 数组与循环控制处理 8.2.1 数组 3.交错数组 • 数组的元素类型不仅可以是.NET平台下支持的任意数据类型任意类型,还可以是数组类型。数组元素的类型为数组时称之为交错数组 (jagged array),此时元素数组的长度不必全都相同。下面的示例分配一个由 Int32 数组组成的数组: • Int32[][] a = new Int32[3][]; • a[0] = new Int32[10]; • a[1] = new Int32[5]; • a[2] = new Int32[7];
8.2 数组与循环控制处理 8.2.1 数组 3.交错数组 • a[0] = new Int32[]{ 2,4,6,8,10,12,14,16,18,20 }; //10个整数组成的数组 • a[1] = new Int32[]{ 1,3,5,7,9 }; //5个整数组成的数组 • a[2] = new Int32[]{ 11,22,33,44,55,66,77 }; //7个整数组成的数组。
8.2 数组与循环控制处理 8.2.2 循环控制结构 • 循环控制结构可以用于重复执行嵌入语句。C#中的循环(迭代)语句有: • while语句 • do语句 • for语句 • foreach语句
8.2 数组与循环控制处理 8.2.2 循环控制结构 1.while 语句 • while 语句按不同条件不执行或至少执行一次语句块。while 语句的一般格式为: • while ( 布尔表达式 ) • { • 语句块; • } • while 语句按下列规则执行: ① 计算布尔表达式。 ② 当布尔表达式值为true时,执行while语句中的语句块,然后程序转到①。 ③ 当布尔表达式值为 false时,结束循环。
8.2 数组与循环控制处理 8.2.2 循环控制结构 1.while 语句 • 下面的控制台应用程序,利用while语句求1+2+3+…+10,并在控制台上输出其和。代码如下: • using System; • class Sum • { • static void Main() • { • Int32 num=1,sum=0; • while (num<11) • { • sum=sum+num; • num++; • } • Console.WriteLine("1+2+3+...+10 ="+sum); • } • } 输出结果如下: 1+2+3+...+10 =55
8.2 数组与循环控制处理 8.2.2 循环控制结构 2.do 语句 • do 语句按不同条件至少执行一次或多次语句块。do 语句的一般格式为: • do • { • 语句块; • } • while (布尔表达式 ) ; • do 语句按下列规则执行: ① 执行语句块一次。 ② 计算布尔表达式的值,当值为true时,程序转到①;否则,结束循环。
8.2 数组与循环控制处理 8.2.2 循环控制结构 • 下面的控制台应用程序,利用do…while语句求1+2+3+…+10,并在控制台上输出其和。代码如下: • using System; • class Sum • { • static void Main() • { • Int32 num=1,sum=0; • do • { • sum=sum+num; • num++; • } • while (num<11); • Console.WriteLine("1+2+3+...+10 ="+sum); • } • } 输出结果同while语句。
8.2 数组与循环控制处理 8.2.2 循环控制结构 3.for 语句 • for 语句计算一个初始化表达式,然后,当某个条件为真时,重复执行相关的嵌入语句并计算一个迭代表达式序列。for 语句的一般格式为: • for(初始化表达式 ; 终止条件布尔表达式 ; 步进语句表达式) • { • 语句块; • } • 其中初始化表达式是为循环变量赋初值,可以有1个或多个,若有多个则用逗号隔开;终止条件布尔表达式是用来决定是否执行语句块;步进语句表达式是按规律改变初始表达式中循环变量的值。这三个表达式都是可选的,缺省某个表达式时,其后的“;”不能省。
8.2 数组与循环控制处理 8.2.2 循环控制结构 3.for 语句 • 下列代码表达了三个表达式都缺省的情况: • for(;;) • { • 语句块; • } • for 语句按如下规则执行: • ① 执行初始化表达式,为循环变量赋初值。 • ② 若没有终止条件布尔表达式,或终止条件布尔表达式值为true时,则直接执行大括号中的语句块,程序转向③;否则,结束循环。 • ③ 执行步进语句表达式,程序转向②。
8.2 数组与循环控制处理 8.2.2 循环控制结构 3.for 语句 • 下面的控制台应用程序,利用for语句求1+2+3+…+10,并在控制台上输出其和。代码如下: • using System; • class Sum • { • static void Main() • { • Int32 num,sum; • for(num=1,sum=0;num<11;num++) • { • sum=sum+num; • } • Console.WriteLine("1+2+3+...+10 ="+sum); • } • } 输出结果同while语句。
8.2 数组与循环控制处理 8.2.2 循环控制结构 4.foreach 语句 • foreach 语句用于遍历一个集合的元素,并对该集合中的每个元素执行一次相关的语句块。foreach 语句的一般格式为: • foreach ( 类型标识符 标识符 in 表达式 ) • { • 语句块; • } • 其中类型标识符和标识符用来声明变量,表达式的类型必须是集合类型,如果该表达式的值为 null,则将引发异常。每执行一次语句块,循环变量就依次取集合中的一个元素代入其中。
8.2 数组与循环控制处理 8.2.2 循环控制结构 4.foreach 语句 • 下面的控制台应用程序,用于按照元素的顺序打印出一个二维数组中的各个元素的值。代码如下: • using System; • class Test • { • static void Main() • { • Double[,] values = {{1.2, 2.3, 3.4, 4.5},{5.6, 6.7, 7.8, 8.9}}; • foreach (Double elementValue in values) • Console.Write(" " + elementValue); • Console.WriteLine(); • } • } 输出结果如下: 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9
8.2 数组与循环控制处理 8.2.3 跳转语句 • 跳转语句用于无条件地在程序间转移控制权。C#中的跳转语句有: • break语句 • continue语句 • goto 语句 • return 语句 • throw 语句 • 跳转语句会将控制转到某个位置,这个位置就称为跳转语句的目标 (target)。 • 当一个跳转语句出现在某个块内,而该跳转语句的目标在该块之外时,就称该跳转语句退出该块。虽然跳转语句可以将控制转到一个块外,但它永远不能将控制转到一个块的内部。
8.2 数组与循环控制处理 8.2.3 跳转语句 1.break 语句 • break 语句直接退出所属的switch、while、do、for 或 foreach 语句。当程序遇到这一语句后,执行紧接在被终止执行的语句后面的语句。通常break语句总是与if语句联在一起。即满足条件时便跳出循环。 • 当多个 switch、while、do、for 或 foreach 语句彼此嵌套时,break 语句只应用于最里层的语句。若需要穿越多个嵌套层,可使用 goto 语句。 • break 语句不能退出 finally 块。当 break 语句出现在 finally 块中时,该 break 语句的目标必须位于同一个 finally 块中,否则将发生编译时错误。块外,但它永远不能将控制转到一个块的内部。
8.2 数组与循环控制处理 8.2.3 跳转语句 1.break 语句 • 下面的控制台应用程序计算1~100之间的和,当数超过100时终止循环。代码如下: • using System; • class Sum • { • static void Main() • { • Int32 num=1,sum=0; • while (true) • { • sum=sum+num; • num++; • if (num>100) break; • } • Console.WriteLine("1+2+3+...+100 ="+sum); • } • } 输出结果如下: 1+2+3+...+100 =5050
8.2 数组与循环控制处理 8.2.3 跳转语句 2.continue 语句 • continue 语句直接结束封闭它的 while、do、for 或 foreach 语句的本次循环,进入下一轮循环。它与前面提到的break语句不同,它并不终止整个循环的执行,而仅仅终止当前这一次循环的运行。 • 如果 continue 语句不是由 while、do、for 或 foreach 语句所封闭的,则发生编译时错误。 • 当多个 while、do、for 或 foreach 语句互相嵌套时,continue 语句只应用于最里层的那个语句。若需要穿越多个嵌套层,可使用 goto 语句。 • continue 语句不能退出 finally 块。当 continue 语句出现在 finally 块中时,该 continue 语句的目标必须位于同一个 finally 块中,否则将发生编译时错误。
8.2 数组与循环控制处理 8.2.3 跳转语句 2.continue 语句 • 下面的控制台应用程序计算数1~100之间的偶数和。代码如下: • using System; • class Sum • { • static void Main() • { • Int32 num,sum=0; • for(num=1;num<101;num++) • { • if (num%2!=0) continue; • sum=sum+num; • } • Console.WriteLine("1到100之间的偶数和为 "+sum); • } • } 输出结果如下: 1到100之间的偶数和为 2550
8.2 数组与循环控制处理 8.2.3 跳转语句 3.goto 语句 • goto 语句将控制无条件的转移到由标签标记的语句。它能用来跳出循环和switch语句。 • 如果当前函数成员中不存在具有给定名称的标签,或者如果 goto 语句不在该标签的范围内,则发生编译时错误。标号与goto语句必须处在同一个函数中。此规则允许使用 goto 语句将控制转移“出”嵌套范围,但是不允许将控制转移“进”嵌套范围。 • goto 语句不能退出 finally 块。当 goto 语句出现在 finally 块中时,该 goto 语句的目标必须位于同一个 finally 块中,否则将发生编译时错误。
8.2 数组与循环控制处理 8.2.3 跳转语句 3.goto 语句 • 下面是一个控制台应用程序示例: • using System; • class Test • { • static void Main(string[] args) • { • string[,] table = {{"Red", "Blue", "Green"},{"Monday", "Wednesday", "Friday"}}; • foreach (string str in table) • { • int row, colm; • for (row = 0; row <= 1; ++row) • for (colm = 0; colm <= 2; ++colm) • if (str == table[row,colm]) • goto done;
8.2 数组与循环控制处理 8.2.3 跳转语句 3.goto 语句 • Console.WriteLine(“{0} 未找到”, str);//显示str的内容未找到 • continue; • done: • Console.WriteLine("在[{1}][{2}]位置找到{0}", str, row, colm); • } • } • } • 输出结果如下: • 在[0][0]位置找到Red • 在[0][1]位置找到Blue • 在[0][2]位置找到Green • 在[1][0]位置找到Monday • 在[1][1]位置找到Wednesday • 在[1][2]位置找到Friday
8.2 数组与循环控制处理 8.2.3 跳转语句 4.return 语句 • return 语句将控制返回到包含return 语句的函数成员的调用方。 • 若函数成员没有返回类型,则return语句后不带任何表达式;或函数成员有返回类型,则return语句后的表达式的计算结果,必须与函数成员返回类型一致。 • return 语句出现在 finally 块中会造成编译时错误。 • 下面是一个控制台应用程序示例: • using System; • class Test • { • static Int64 sum(Int32 num)
8.2 数组与循环控制处理 8.2.3 跳转语句 4.return 语句 • { • Int64 mysum=0; • for (Int32 i = 1; i <=num; i++) • mysum = mysum + i; • return mysum; • } • static void Main(string[] args) • { • Console.WriteLine("从1到100的和为 "+sum(100)); • } • } • 输出结果如下:从1到100的和为 5050 5.throw语句 • throw 语句引发一个异常。
8.3 数据类型的转换 • 类型转换是将数据从一种类型更改为另一种类型的过程。例如,可以将字符串“1234”转换为一个数字。可以将任意类型的数据转换为 String 类型。 • 某些类型转换永远不会成功。例如,字符串"张三"不能转换为整型数据。
8.3 数据类型的转换 1.基本概念 • 类型转换有可能扩大,也有可能收缩。 • 扩大转换就是指将数据类型A的值转换为数据类型B,并且保证数据类型B可以容纳数据类型A的任何可能值,例如,转换Int32型为Int64型,转换派生类型为基类型。 • 收缩转换就是指将数据类型A的值转换为数据类型B,但数据类型B无法容纳数据类型A的某些可能值,例如,转换Int64型为Int32型,转换基类型为派生类型。
8.3 数据类型的转换 1.基本概念 • 扩大转换永远不会溢出,并且总是成功的;而收缩转换必然往往伴有信息的丢失,并有可能失败。 • 两种转换类型都可以是显式(使用数据类型标识符)或隐式(不使用数据类型标识符)的。例如: • Int32 a = 123; • Int64 b = a; // 隐式转换 • Int32 c = (Int32) b; // 显式转换
8.3 数据类型的转换 1.基本概念 • 有效的显式转换即使会导致信息的丢失,也始终是成功的。而隐式转换只有在转换过程不丢失数据的情况下才能成功;否则,它们就会失败并生成编译或运行时错误。 • .NET类库中的 Form 窗体类有一个属性Controls,这是一个集合,其中元素是窗体上的所有控件,如:TextBox。窗体上的控件都以Control为基类。
8.3 数据类型的转换 1.基本概念 • 下面代码将窗体上的一般控件显式收缩转换为TextBox控件。 • foreach(Control ctl in Controls) • { • //从Control基类显式转变为TextBox派生类 • TextBox tx = (TextBox)ctl; • } • 执行中许多非TextBox控件向TextBox控件转换时会失败。
8.3 数据类型的转换 1.基本概念 • 当原始数据类型在目标转换类型中没有明显的对应类型时,就会发生丢失数据的转换。例如,字符串"Fred"不能转换为一个数字。在这些情况下,类型转换函数将返回一个默认值。对于字符串类型,默认值为"";对于整型类型,默认值为数字 0。 • 常用的ToString方法就是显式转换的典型代表,因为它是所有数据类型的根类object的方法,所以所有数据类型都可以向字符串类型转换。
8.3 数据类型的转换 2.Convert类 • Convert是一个专用来将一个基本数据类型转换为另一个基本数据类型的类,因为是静态类,不需要实例化就可以直接使用。 • 例如: • Int64 I = 123; • Int32 i = Convert.ToInt32(I);
8.3 数据类型的转换 3.装箱和拆箱 • 装箱和拆箱的概念是 C# 的类型系统的核心。它在值类型和引用类型之间的架起了一座桥梁,使得任何值类型的值都可以转换为 object 类型的值,反过来转换也可以。从值类型转换为引用类型称为装箱,反之,称为拆箱。 • 装箱和拆箱使我们能够统一地来考察类型系统,其中任何类型的值最终都可以按对象处理
8.3 数据类型的转换 3.装箱和拆箱 • 下面是装箱和拆箱转换的示例,代码如下: • Int32 i = 10; //i是值类型 • object o = i; //装箱 • i = (Int32)o; //拆箱 • MessageBox.Show(i.ToString());//整型数转换成字符串 • 装箱操作如图8-2所示,拆箱操作如图8-3所示。
o:object i:Int32 10 i:Int32 10 拆箱 装箱 i:Int32 10 Int32 10 o:object 8.3 数据类型的转换 3.装箱和拆箱 • 图8-2装箱操作 图8-3拆箱操作
8.3 数据类型的转换 3.装箱和拆箱 • 一个装箱操作包括两个步骤:首先分配一个对象实例,然后将值类型的值复制到该实例中。 • 一个拆箱操作包括两个步骤:首先检查对象实例是否为给定值类型的一个装了箱的值,然后将该值从实例中复制出来。
8.4 改造ShowDialog方法 我们已经建立了数据维护对象体系,如图8-4所示。
8.4 改造ShowDialog方法 • 实现了frm增改学生的ShowDialog方法。 • 类似地,要为frm增改课程等实现ShowDialog方法。不仅工作量较大,且容易出错,将来也不利于进行代码维护。 • 若能在frm增改中实现ShowDialog方法,则可以惠及其派生类,起到“一劳永逸”的功效。
8.4 改造ShowDialog方法 8.4.1 提示必要信息 • 在frm增改中实现ShowDialog方法所面临的最大问题是:不知道其派生类中需要处理的输入控件。而处理的关键是要抓住被处理的控件。 • 窗体对象包含有一个控件集合Controls,控件集合Controls中包含了窗体上所有的控件(Control),如TextBox和DateTimePicker控件等。如果能对Controls中所有的Control进行遍历,就能进行适当的处理了。