3.6 面向对象编程
随着计算机软件技术的飞速发展与软件开发需求的提高,已逐渐要求按照工程化的方法和原则来组织软件开发工作,而传统的面向过程的程序设计方法越来越暴露出它的不足之处。例如:
● 功能与数据分离,要保持功能与数据的相容性非常困难;
● 基于模块的设计方式,使软件修改困难;
● 自顶向下的设计方法,限制了软件的可重用性,降低了开发效率,也导致了设计出来的系统难以维护。
为了解决结构化程序设计的问题,面向对象的设计技术应运而生,它是一种强有力的软件设计方法。
在程序的设计中,算法总是与特定的数据结构密切相关的,算法含有对数据结构的访问,特定的算法只适用于特定的数据结构,因此算法与数据结构在编程中应该是一个密不可分的整体,这个整体叫对象。
面向对象的程序设计多采用可视化的方式,通过类、对象、继承、多态等机制形成一个完善的编程体系。
C#语言是一种现代、面向对象的语言。面向对象程序设计方法提出了一个全新的概念——类。它的主要思想是将数据(数据成员)及处理这些数据的相应方法(函数成员)封装到类中,类的实例则称为对象。这就是我们常说的封装性。
3.6.1 类
现实生活中的类是人们对客观对象不断认识而产生的抽象概念,而对象则是现实生活中的一个个实体。面向对象程序设计的类概念从本质上和人们现实生活中的类概念是相同的。
可以把类比做一种蓝图,而对象则是根据蓝图所创建的实例;可以把类比做生产模具,而对象则是由这种模具产生的实例(产品)。所以人们又把对象叫做类的实例。类是对事物的定义,而对象则是该事物本身。类是一种数据类型。在C#中,类分为两种,一种是由系统提供预先定义的,这些类在.NET框架类库中,另一种是用户定义的数据类型。
一般希望类中一些数据不被随意修改,只能按指定方法修改,即隐蔽一些数据。同样一些函数也不希望被其他类程序调用,只能在类内部使用。如何解决这个问题呢?可用访问权限控制字。常用的访问权限控制字如下:private(私有),public(公有)。在数据成员或函数成员前增加访问权限控制字,可以指定该数据成员或函数成员的访问权限。
私有数据成员只能被类内部的函数使用和修改,私有函数成员只能被类内部的其他函数调用。类的公有函数成员可以被类的外部程序调用,类的公有数据成员可以被类的外部程序直接使用和修改。公有函数实际是一个类和外部通信的接口,外部函数通过调用公有函数,按照预先设定好的方法修改类的私有成员。
由此可以看出封装有两个意义,第一是把数据和处理数据的方法同时定义在类中。第二是用访问权限控制字使数据隐蔽。
1. 类的组成
类是一组具有相同数据结构和相同操作的对象的集合。
用class定义类,声明类的形式为:
[附加声明][访问修饰符]class类名称[:[基类][,接口序列]] { [字段声明] [构造函数] [方法] [事件] }
类的成员包括以下类型。
1)数据成员
(1)局部变量:在for、switch等语句中和类方法中定义的变量,只在指定范围内有效。
(2)字段:即类中的变量或常量,包括静态字段、实例字段、常量和只读字段。
用修饰符static声明的字段为静态字段。不管包含该静态字段的类生成多少个对象或根本无对象,该字段都只有一个实例,必须采用如下方法引用静态字段:
类名.静态字段名
如果类中定义的字段不使用修饰符static,则该字段为实例字段。每创建该类的一个对象,就会在对象内创建一个该字段实例;若创建它的对象被撤销,则该字段对象也被撤销。实例字段采用如下方法引用:
实例名.实例字段名
(3)事件:事件是类的成员,是对象发送的消息,发送信号通知客户发生了操作。这个操作可能是由鼠标单击引起的,也可能是由某些其他的程序逻辑触发的。事件有两种角色,一个是事件发送方,一个是事件接收方。事件的发送方不需要知道哪个对象或者方法接收它引发的事件,只需要知道在它和接收方之间存在的中介即可。
2)函数成员
(1)方法成员:包括静态方法和实例方法。它们可以是实例方法,也可以是静态方法。
(2)属性:属性是在客户机上访问的函数组,其访问方式与访问类的公共字段相似。
(3)构造函数:在实例化对象时自动调用的函数。它们必须与所属的类同名。
(4)索引指示器:允许对象以数组或集合的方式进行索引。
(5)运算符:最简单的就是“+”,其他的已在前面介绍过。
类还包含多类访问修饰符。访问修饰符用于指定类成员的可访问性,C#访问修饰符常用的有private、protected、public、internal、statics和new 6种,它们的具体用途如表3-9所示。
表3-9 类的主要访问修饰符
下面给出一个类的实例。
【例3-15】 类的定义示例。
(1)创建项目。打开文件菜单,依次选择“新建”→“项目”命令,打开“新建项目”对话框,新建控制台应用程序ClassDemo。
(2)在项目中新建类文件Child,输入以下代码:
public class Child {//定义字段 private int age; private string name; // 不带参数的构造函数 public Child() { name = "none"; } // 带参数的构造函数 public Child(string name, int age) { this.name = name; this.age = age; } // 定义输出方法 public void PrintChild() { Console.WriteLine("{0}, {1} years old.", name, age); } }
在Program.cs的Main函数中输入以下代码:
public static void Main(string[] args) { //使用new关键字创建对象,new后是调用的构造函数 Child child1 = new Child("Zhang San", 11); Child child2 = new Child("Li Si", 10); Child child3 = new Child(); // 显示结果 Console.Write("Child #1: "); child1.PrintChild(); Console.Write("Child #2: "); child2.PrintChild(); Console.Write("Child #3: "); child3.PrintChild();} Console.ReadLine(); }
(3)运行调试程序。
单击“调试”菜单,选择“启动调试”命令,或单击工具栏上的“启动调试”按钮或按【F5】键来运行程序。运行的结果如图3-29所示。
图3-29 运行结果
注意:在许多方面,可以把C#中的结构看做是缩小的类。它们基本上与类相同,但是更适合于把一些数据组合起来的场合。它们与类的区别在于,结构是值类型,不是引用类型。它们存储在堆栈中或存储为内联,其生存期的限制与简单的数据类型一样。结构不支持继承,并且结构的构造函数的工作方式有一些区别,尤其是编译器总是提供一个无参数的默认构造函数,这是不允许替换的。
2. 构造函数
C#构造函数是一种特殊的成员函数,它主要用于为对象分配存储空间,对数据成员进行初始化。
C#构造函数具有一些特殊的性质。
(1)C#构造函数的名字必须与类同名。
(2)C#构造函数没有返回类型,它可以带参数,也可以不带参数。
(3)声明类对象时,系统自动调用构造函数,构造函数不能被显式调用。
(4)C#构造函数可以重载,从而提供初始化类对象的不同方法。
(5)若在声明时未定义构造函数,系统会自动生成默认的构造函数,此时构造函数的函数体为空。
(6)静态构造函数,用static修饰,用于初始化静态变量,一个类只允许有一个构造函数,在类实例化时加载,这时修饰符public、private失去作用。
(7)可以使用public、protected、private修饰符。
例如,上节定义Child类的构造函数如下:
public Child() { name = "none"; }
在C#语言中,同一个类中的函数,如果函数名相同,而参数类型或个数不同,会被认为是不同的函数,这叫函数重载。仅返回值不同,不能看做是不同的函数。这样,可以在类定义中,定义多个构造函数,名字相同,参数类型或个数不同。根据生成类的对象方法不同,调用不同的构造函数。例如,Child类的另一构造函数如下所示:
public Child(string name, int age) { this.name = name; this.age = age; }
当用语句Child child3=new Child()生成对象时,调用无参数的构造函数。而用Child child1=new Child("Zhang San", 11)语句生成Child类对象时,将自动调用以上带参数的构造函数。请注意如何把参数传递给构造函数。
3. 方法
方法是类中用于执行计算或其他行为的成员。所有方法都必须定义在类或结构中。
方法的声明格式如下:
属性 方法修饰符 返回类型 方法名(形参列表){方法体}
方法修饰符包括new、public、protected、internal、private、static、virtual、sealed、override、abstract和extern。返回类型可以是任何合法的C#数据类型,也可以是void,即无返回值。形参列表的格式为“(形参类型 形参1,形参类型 形参2,...)”,可以有多个形参。
C#语言的方法可以使用以下4种参数。
● 值参数:不含任何修饰符。
● 引用参数:以ref修饰符声明。
● 输出参数:以out修饰符声明。
● 数组参数:以params修饰符声明。
1)值参数
当用值参数向方法传递参数时,程序给实参的值做一份拷贝,并且将此拷贝传递给该方法,被调用的方法不会修改实参的值,所以使用值参数时,可以保证实参的值是安全的。如果参数类型是引用类型,如是类的引用变量,则拷贝中存储的也是对象的引用,所以拷贝和实参引用同一个对象。通过这个拷贝,可以修改实参所引用的对象中的数据成员。
【例3-16】 类的示例一。
(1)创建项目。打开文件菜单,依次选择“新建”→“项目”命令,打开“新建项目”对话框,新建控制台应用程序ClassDemo1。
(2)在项目中新建类文件Person,输入以下代码:
using System; namespace e1//定义以下代码所属命名空间 { class Person { private String name="cody";//类的数据成员声明 private int age=12; public void Display()//类的方法(函数)声明,显示姓名和年龄 { Console.WriteLine("姓名:{0},年龄:{1}",name,age); } public void SetName(string PersonName)//指定修改姓名的方法(函数) { name=PersonName; } public void SetAge(int PersonAge)//指定修改年龄的方法(函数) { age=PersonAge; } public Person(string Name,int Age)//构造函数,函数名和类同名,无返回值 { name=Name; age=Age; } public Person()//类的构造函数重载 { name="Tom"; age=12; } }
在Main()中输入以下代码:
class ClassDemo1 { static void Main(string[]args) { Person OnePerson=new Person("Alice",30);//生成类的对象 OnePerson.Display(); //下句错误,在其他类(Class1类)中,不能直接修改Person类中的私有成员。 //OnePerson.name="Alice"; //只能通过Person类中公有方法SetName修改Person类中的私有成员name。 OnePerson.SetName("Alice"); OnePerson.SetAge(40); OnePerson.Display(); OnePerson=new Person(); OnePerson.Display(); } } }
运行结果如图3-30所示。
图3-30 运行效果
2)引用参数
有时在方法中,需要修改或得到方法外部的变量值。C语言用向方法传递实参指针来达到目的,而C#语言用引用参数来达到目的。当用引用参数向方法传递实参时,程序将把实参的引用,即实参在内存中的地址传递给方法,方法通过实参的引用,修改或得到方法外部的变量值。引用参数以ref修饰符声明。注意在使用前,实参变量要求必须被设置初始值。
3)输出参数
为了把方法的运算结果保存到外部变量,因此需要知道外部变量的引用(地址)。输出参数用于向方法传递外部变量引用(地址),所以输出参数也是引用参数。在方法返回后,传递的变量被认为经过了初始化。值参数、引用参数和输出参数的使用见下例。
【例3-17】 类的示例二。
(1)创建项目。打开文件菜单,依次选择“新建”→“项目”命令,打开“新建项目”对话框,新建控制台应用程序ClaaDemo2。
(2)在项目中新建类文件Class1,输入以下代码:
using System; class g{public int a=0;}//类定义 class Class1 { public static void F1(ref char i)//引用参数 { i='b';} public static void F2(char i)//值参数,参数类型为值类型 { i='d';} public static void F3(out char i)//输出参数 { i='e';} public static void F4(string s)//值参数,参数类型为字符串 { s="xyz";} public static void F5(g gg)//值参数,参数类型为引用类型 { gg.a=20;} public static void F6(ref string s)//引用参数,参数类型为字符串 { s="xyz";} }
在Main中输入以下代码:
static void Main(string[] args) { char a='c'; string s1="abc"; lass1 c1 = new Class1(); c1.F2(a);//值参数,不能修改外部的a Console.WriteLine(a);//因a未被修改,显示c c1.F1(ref a);//引用参数,函数修改外部的a的值 Console.WriteLine(a);//a被修改为b,显示b Char j; c1.F3(out j);//输出参数,结果输出到外部变量j Console.WriteLine(j);//显示e c1.F4(s1);//值参数,参数类型是字符串,s1为字符串引用变量 Console.WriteLine(s1);//显示:abc,字符串s1不被修改 g g1=new g(); c1.F5(g1);//值参数,但实参是一个类引用类型变量 Console.WriteLine(g1.a.ToString());//显示:20,修改对象数据 c1.F6(ref s1);//引用参数,参数类型是字符串,s1为字符串引用变量 Console.WriteLine(s1);//显示:xyz,字符串s1被修改 }
运行效果如图3-31所示。
图3-31 运行效果
4)数组参数
数组参数使用params说明,如果形参表中包含了数组参数,那么它必须是参数表中最后一个参数,数组参数只允许是一维数组。比如,string[]和string[][]类型都可以作为数组型参数。
【例3-18】 数组参数示例。
(1)创建项目。打开文件菜单,依次选择“新建”→“项目”命令,打开“新建项目”对话框,新建控制台应用程序ClassDemo3。
(2)在项目中新建类文件Class1,输入以下代码:
using System; class Class1 { public void F(params int[]args)//数组参数,有params说明 { Console.Write("Array contains{0}elements:",args.Length); foreach (int i in args) Console.Write(" {0}",i); Console.WriteLine(); } static void Main(string[] args) { Class1 c1=new Class1(); int[] a = { 1, 2, 3 }; c1.F(a);//实参为数组类引用变量a c1.F(10, 20, 30, 40);//等价于F(new int[] {60,70,80,90}); c1.F(new int[] { 60, 70, 80, 90 });//实参为数组类引用 c1.F();//等价于F(new int[] {}); c1.F(new int[] { });//实参为数组类引用,数组无元素 Console.ReadLine(); } }
运行结果如图3-32所示。
图3-32 运行效果
在类创建对象后,实例方法才能被使用,使用格式为:
对象名.实例方法名
例如:
c1.F(a);
在C#语言中,如果在同一个类中定义的函数名相同,而参数类型或参数个数不同,则认为是不相同的函数。仅返回值不同,不能看做不同函数,这叫做函数的重载。
4. 属性
属性不是字段,但必然和类中的某个或某些字段相联系,属性定义了得到和修改相联系的字段的方法。C#中的属性更充分地体现了对象的封装性,即不直接操作类的数据内容,而是通过访问器进行访问,借助于get和set方法对属性的值进行读写。也就是按属性指定的get方法和set方法对字段进行读写。属性本质上是方法。访问属性值的语法形式和访问一个变量基本一样,这使访问属性就像访问变量一样方便,并符合习惯。
下面定义一个描述个人情况的类Person,其中字段name和age是私有字段,记录姓名和年龄,外部通过公有方法SetName和SetAge修改这两个私有字段。现在用属性来描述姓名和年龄。
【例3-19】 个人情况的类Person示例。
(1)创建项目。打开文件菜单,依次选择“新建”→“项目”命令,打开“新建项目”对话框,新建控制台应用程序ClassDemo5。
(2)在项目中新建类文件Class1,输入以下代码:
using System; public class Person { private string P_name="张三";//P_name是私有字段 private int P_age=12;//P_age是私有字段 public void Display()//类的方法声明,显示姓名和年龄 { Console.WriteLine("姓名:{0},年龄:{1}",P_name,P_age); } public string Name//定义属性Name { get { return P_name;} set { P_name=value;} } public int Age//定义属性Age { get { return P_age;} set { P_age=value;} } }
在Main中输入以下代码:
public static void Main() { Person OnePerson=new Person(); OnePerson.Name="张三";//value="张三",通过set方法修改变量P_Name string s=OnePerson.Name;//通过get方法得到变量P_Name值 OnePerson.Age=20;//通过定义属性,既保证了姓名和年龄按指定方法修改 int x=OnePerson.Age;//语法形式和修改、得到一个变量基本一致,符合习惯 OnePerson.Display(); Console.ReadLine(); }
运行结果如图3-33所示。
图3-33 运行效果
在属性的访问声明中,只有set访问器时表明属性的值只能进行设置而不能读出,只有get访问器时表明属性的值是只读的不能改写,同时具有set访问器和get访问器时表明属性的值的读写都是允许的。
虽然属性和字段的语法比较类似,但由于属性本质上是方法,因此不能把属性当做变量那样使用,也不能把属性作为引用型参数或输出参数来进行传递。
5. 事件
Windows操作系统把用户的动作都看做消息,C#中称做事件。例如,用鼠标左键单击按钮,发出鼠标单击按钮事件。Windows操作系统负责统一管理所有的事件,把事件发送到各个运行程序。各个程序用事件函数响应事件,这种方法也叫事件驱动。也就是说,事件为类和类的实例提供了向外界发送通知的能力。
C#语言使用组件编制Windows应用程序。组件本质上是类。在组件类中,预先定义了该组件能够响应的事件,以及对应的事件函数。一旦该事件发生,将自动调用自己的事件函数。例如,按钮类中定义了单击事件Click和单击事件函数。一个组件中定义了多个事件,应用程序中不必也没必要响应所有的事件,而只需响应其中很少事件即可,程序员编制相应的事件处理函数,用来完成需要响应的事件所应完成的功能。现在的问题是,第一,如何把程序员编制的事件处理函数和组件类中预先定义的事件函数联系起来;第二,如何使不需响应的事件无动作。
在C#中,事件首先代表事件本身,如按钮类的单击事件。同时,事件还代表类引用变量,可以代表程序员编制的事件处理函数,把事件和事件处理函数联系在一起。下面的例子介绍了定义一个Button组件的部分代码。代码如下:
public delegate void EventHandler(object sender,EventArgs e);//代表声明 //EventHandler可以代表没有返回值,参数为(object sender,EventArgs e)的函数 public class Button:Control//定义一个按钮类Button组件 {…//按钮类Button其他成员定义 public event EventHandler Click;//声明一个事件Click,是代表类引用变量 protected void OnClick(EventArgs e)//Click事件发生,自动触发OnClick方法 { if(Click!=null)//如果Click已代表了事件处理函数,执行这个函数 Click(this,e); } public void Reset() { Click=null;} }
在这个例子中,Click事件发生,应有代码保证自动触发OnClick方法。Click是类Button的一个事件,同时也是代表EventHandler类的引用变量。如令Click代表事件处理函数,该函数完成Click事件应完成的功能,则当Click事件发生时,执行事件处理函数。
3.6.2 常用类
在使用C#进行程序设计的时候,使用一些系统提供的类将极大地减少程序员的代码编写量,下面将介绍几个常用的类。
1. Convert
Convert类中提供了一些静态方法,用来把一种类型数据转换为另一种类型数据。例如,Convert.ToSingle(textBox1.Text)可把字符串textBox1.Text转换为单浮点数。Convert.ToString(3.14)可把单浮点数3.14转换为字符串。其他转换函数还有ToInt、ToInt16等。
Convert类在C#的编程中占有非常重要的位置,大量的数据在处理的时候都要面临类型转换的问题。下面给出Convert类的示例。
【例3-20】 加法计算器。
下面程序实现了简单的加法操作功能,以说明字符串类型转换在程序设计中的应用。具体步骤如下。
(1)在“文件”菜单上,指向“新建”,然后单击“项目”。
(2)确保“Windows窗体应用程序”模板处于选中状态,在“名称”字段中,输入“AddProject”,然后单击“确定”按钮。
在Windows窗体设计器中会显示一个Windows窗体。这是应用程序的用户界面。
(3)在“视图”菜单上,单击“工具箱”以使控件列表可见。
(4)展开“公共控件”列表,并将两个“文本框”控件拖到窗体中,分别默认名称TextBox1和TextBox2。然后拖放两个标签,分别修改其“Text”属性为“+”和“=”。
(5)还要从工具箱“公共控件”列表中将一个按钮拖到窗体上靠近标签的位置,设计完成的窗体如图3-34所示。
图3-34 “加法计算器”窗口
(6)双击此新按钮以打开代码编辑器。C#已插入了一个名为button1_Click的方法,输入以下代码:
if (this.textBox2.Text == ""||this.textBox3.Text == "") { MessageBox.Show("还缺少操作数!"); return; } inta,b,c; //将标签和文本框的Text转换成整型数 a=Convert.ToInt2(textBox2.Text); b= Convert.ToInt2 (textBox3.Text); c=a+b;//若答案正确 this.textBox1.Text=c.ToString();
选择“调试”→“开始执行”命令,分别在两个文本框中输入3和5,程序的运行结果为8,如图3-35所示。
图3-35 加法运算器运行结果窗口
2. String
System.String是一个类,专门用于存储字符串,并可对字符串进行许多操作。String类提供了很多用于安全地创建、操作和比较字符串的方法。由于这种数据类型非常重要,C#提供了它自己的关键字和相关的语法,以便使用这个类处理字符串。
(1)可以使用以下的方式来声明并初始化一个字符串变量:
String text = "字符串的使用"; Console.WriteLine(text);
(2)注意字符串的直接指定必须使用""来包括文字,字符串的每个字符是使用Unicode字符来构建的。在构建一个字符串对象变量之后,可以直接在输出中指定变量名称来输出字符串。
(3)字符串的串联在C#中可以直接使用“+”运算符,“+”本来是加法运算符,而它被改写(Override)为可以直接用于字符串的串联。
String msg = "helle!"; msg = msg + "C#程序设计!"; Console.WrieLine (msg);
这一段程序代码会在命令行模式上显示“hello!C#程序设计!”。
字符串在C#中以String类的一个实例存在,所以每个字符串对象本身会拥有几个可操作的方法。常用的几个方法如表3-10所示。
表3-10 String对象常用的几个方法
下面从几个方面来介绍String类的用法。
1)获取字符串长度
例如:
string str; str=”This is a Book.” //查看str的长度,属性将返回一个整数 Console.WrieLine(str.Length);
2)子字符串
子字符串是包含在字符串中的任意字符序列。使用Substring方法可以基于原始字符串的一部分创建新字符串。可以使用IndexOf方法搜索子字符串的一个或多个匹配项。使用Replace方法可将指定子字符串的所有匹配项替换为一个新字符串。与Substring方法一样,Replace实际上返回的也是新字符串,而不会修改原始字符串。因此需注意此时新字符串相当于是在原字符串的副本上进行操作,必须使用赋值语句重新赋值,否则原字符串是不会改变的。
【例3-21】 字符串示例。
新建控制台程序项目“ex_substring”,具体代码如下:
static void Main(string[] args) { string s3 = "Visual C# Express"; System.Console.WriteLine(s3.Substring(7, 2)); // 输出:"C#" System.Console.WriteLine(s3.Replace("C#", "Basic")); // 输出:"Visual Basic Express" // Index values are zero-based int index = s3.IndexOf("C"); // 输出=7 }
运行效果如图3-36所示。
图3-36 运行效果
3)比较字符串
当需要对两个字符串的值进行比较和排序,而不需要考虑语言惯例时,可使用基本的序号比较。基本的序号比较(Ordinal)是区分大小写的,这意味着两个字符串的字符必须完全匹配,即“and”不等于“And”或“AND”。比较字符串的方法如表3-11所示。
表3-11 比较字符串的方法
【例3-22】 比较两个字符串示例。
新建控制台程序项目“ex_bjstring”,添加具体代码如下:
static void Main(string[] args) { string MyString = "Hello World!"; Console.WriteLine(String.CompareOrdinal(MyString, "hello world!")); String OtherString = "Hello World!"; int MyInt = MyString.CompareTo(OtherString); Console.WriteLine(MyInt); Console.WriteLine(MyString.Equals("Hello World")); Console.WriteLine(MyString.StartsWith("Hello")); Console.WriteLine(MyString.EndsWith("Hello")); Console.WriteLine(MyString.IndexOf('l')); Console.WriteLine(MyString.LastIndexOf('l')); Console.ReadLine(); }
运行效果如图3-37所示。
图3-37 运行效果
4)拆分字符串
下面的代码示例演示了如何使用String的Split方法分割字符串。作为输入,Split采用一个字符数组指示哪些字符被用做分隔符。本示例中使用了空格、逗号、句点、冒号和制表符。一个含有这些分隔符的数组被传递给Split,并使用结果字符串数组分别显示句子中的每个单词。
【例3-23】 拆分字符串示例。
新建控制台程序项目“ex_split”,添加具体代码如下:
class ex_split { static void Main(string[] args) { char[] delimiterChars = { ' ', ',', '.', ':', '\t' }; string text = "one\ttwo three:four,five six seven"; System.Console.WriteLine("Original text: '{0}'", text); string[] words = text.Split(delimiterChars); System.Console.WriteLine("{0} words in text:", words.Length); foreach (string s in words) { System.Console.WriteLine(s); } // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } }
运行效果如图3-38所示。
图3-38 运行效果
其中的代码:
System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey();
同语句Console.ReadLine()一样,都是为了让程序停留等待,否则程序运行完后会直接退出。
5)确定字符串是否表示数值
若要确定字符串是否是指定数值类型的有效表示形式,可使用静态的TryParse方法。该方法由所有基元数值类型,以及诸如DateTime和IPAddress这样的类型实现。
【例3-24】 演示如何确定“108”是否为有效的int类型。
新建控制台程序项目“ex_str_int”,添加具体代码如下:
static void Main(string[] args) { int i = 0; string s = "108"; bool result = int.TryParse(s, out i); Console.WriteLine(result); Console.ReadLine(); }
如果字符串包含非数值字符或者所包含的数值对于指定的特定类型而言太大或太小,TryParse都将返回false,并将out参数设置为零。否则,它将返回true,并将out参数设置为字符串的数值。运行效果如图3-39所示。
图3-39 运行效果
6)如何将字符串数据转换为日期类型
在用户向文本框输入数据的过程中,所有的内容均为字符串型,但当用户输入日期时,又会面临字符串数据向日期数据转换的问题。
【例3-25】 字符串转换为日期型数据。
新建控制台程序项目“ex_date”,添加具体代码如下:
static void Main(string[] args) { string date = "01/08/2008"; DateTime dt = Convert.ToDateTime(date); Console.WriteLine("Year: {0}, Month: {1}, Day: {2}", dt.Year, dt.Month, dt.Day); IFormatProvider culture = new System.Globalization.CultureInfo("fr-FR", true); DateTime dt2 = DateTime.Parse(date, culture, System.Globalization.DateTimeStyles.A ssumeLocal); Console.WriteLine("Year: {0}, Month: {1}, Day {2}", dt2.Year, dt2.Month, dt2.Day); }
运行效果如图3-40所示。
图3-40 运行效果
3. StringBuiler
StringBuilder类是动态字符串,StringBuilder的对象创建以后可以对其进行删除和增加,而且是在同一个对象上进行的,mystr.Append(" new")执行以后并没有创建新的对象,它的结果还是保存在原来的地方。这点与string类不一样。StringBuilder类的构造方法为:
System.Text.StringBuilder变量名= new System.Text.StringBuilder("");
例如:
StringBuilder obj=new StringBuilder(“biancdheng”); StringBuilder obj2=new StringBuilder(“biancheng”,18);//指定为18字节。
StringBuilder提供了Append方法用以实现字符串的连接,在这方面,它与“+”是大致相同的。例如:
StringBuilder mystr =new StringBuilder("Larger than me"); Console.WriteLine(mystr.Append("new")); //mystr后面增加"new" Console.WriteLine(mystr.AppendLine(" loveme"));
注意:不能把StringBuilder转换为String,如果要把StringBuilder的内容输出为String,唯一的方法是使用ToString ()。
4. DateTime
DateTime类中提供了一些静态方法,可以用来得到日期、星期和时间。下面是一些常用的方法。
得到日期和时间,并转换为字符串。
String s=DateTime.Now.ToString(); //或DateTime.Today.ToString()
得到年、月和日期。
int y=DateTime.Now.Year;//得到年 int m=DateTime.Now.Month;//得到月 int d=DateTime.Now.Day;//得到日期 String s=DateTime.Now.DayOfWeek.ToString(); //英文表示的星期
得到小时、分和秒。
int h=DateTime.Now.Hour; //得到小时 int m=DateTime.Now.Minute; //得到分 int s=DateTime.Now.Second; //得到秒
定义一个DateTime类对象,表示1999年1月13日3时57分32.11秒。
System.DateTime moment=new System.DateTime(1999,1,13,3,57,32,11);
加法和减法(减法请读者自己完成)。
System.DateTime dTime=new System.DateTime(1980,8,5);//1980年8月5日 //时间间隔,17天4小时2分1秒 System.TimeSpan tSpan=new System.TimeSpan(17,4,2,1); System.DateTime result=dTime+tSpan; //结果是1980年8月22日4:2:1 AM
5. Math
数学类包含了许多数学函数,如sin、cos、exp、abs等。Math类是一个工具类,它在解决与数学有关的一些问题时有着非常重要的作用。
这个类有两个静态属性,即E和PI。E代表数学中的e,即2.7182818,而PI代表派pi,即3.1415926。
引用时,用法为Math.E和Math.Pi。
下面列举这个类的一些常用方法。
(1)abs方法用来求绝对值。用法为:
int a = Math.Abs(124);
(2)exp求e的a次幂。用法为:
double c = Math.Exp(5);
(3)floor返回最大的小于a的整数。用法为:
Doubole b=Math.Floor(5.2);
(4)log返回lna。用法为:
double c = Math.Log(5);
(5)sqrt求a的平方根。用法为:
double s=Math.Sqrt(7);
注意:在使用Math类时需要在命名空间中加入System.Math。
6. Random
Random是一个产生伪随机数字的类,即一种能够产生满足某些随机性统计要求的数字序列的设备。
在C#中Random是一个独立的类,而不像VB中那样是一个Math的函数,如果学过VB的设计人员要特别注意。
Random类的构造函数有两种,一个是New Random(),另外一个是New Random(Int32)。前者是根据触发时的系统时间作为种子,来产生一个随机数字,后者可以自己设定触发的种子。如果触发Randm函数间隔时间很短,就有可能造成产生一样的随机数。因为伪随机的数字,在Random的内部产生机制中还是有一定规律的,它们是用一种确定的数学算法选择的,并非是真正意义上的完全随机。但是从实用的角度而言,其随机程度已足够了。
Random类产生随机数字的主要办法是Next(),如Next(100)可产生一个比100小的正整数,Next(1,100)可在1到100中间产生一个随机数字,而利用Ticks(以100毫秒作为基础单位的时间数量单位)来产生随机数,还是存在合理性的。
【例3-26】 创建随机数示例。
新建控制台程序consol_random,在Main()中输入以下代码:
static void Main(string[] args) { Random rand = new Random(); //不带参数 Console.WriteLine("Five random integers without parameter:" + "\n"); for (int ctr = 0; ctr <= 4; ctr++) Console.WriteLine(String.Format("{0,8:N0}", rand.Next())+"\n"); //带参数 Console.WriteLine("Five random integers between 50 and 100:" + "\n"); for (int ctr = 0; ctr <= 4; ctr++) Console.WriteLine(String.Format("{0,8:N0}", rand.Next(50, 101))+"\n"); Console.ReadLine(); }
运行结果如图3-41所示。
图3-41 Random示例运行效果