5.1 类
类(Class)是对某种类型的对象定义变量和方法的原型。它表示对现实生活中一类具有共同特征的事物的抽象,是面向对象编程的基础。
本节将学习类的相关知识。
5.1.1 类的概述
类的实质是一种数据类型,类似于int、char等基本类型,不同的是它是一种复杂的数据类型。因为它的本质是类型,而不是数据,所以不存在于内存中,不能被直接操作,只有被实例化为对象时,才会变得可操作。
类是对现实生活中一类具有共同特征的事物的抽象。如果一个程序里提供的类型与应用中的概念有直接的对应,这个程序就会更容易理解,也更容易修改。一组经过很好选择的用户定义的类会使程序更简洁。此外,它还能使各种形式的代码分析更容易进行。特别地,它还会使编译器有可能检查对象的非法使用。
类的内部封装了方法,用于操作自身的成员。类是对某种对象的定义,具有行为,它描述一个对象能够做什么以及做的方法,它们是可以对这个对象进行操作的程序和过程。它包含有关对象动作方式的信息,包括它的名称、方法、属性和事件。
5.1.2 类的面向对象的概述
面向对象编程(Object Oriented Programming,OOP)是一种计算机编程架构。OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成,OOP达到了软件工程的三个目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其他对象发送信息。面向对象一直是软件开发领域内比较热门的话题,首先,面向对象符合人类看待事物的一般规律。其次,采用面向对象方法可以使系统各部分各司其职、各尽所能,为编程人员敞开了一扇大门,使其编写的代码更简洁,更易于维护,并且具有更强的可重用性。
5.1.3 类的声明及其类成员
1.类的声明
在C#中,使用class关键字来声明类,其语法如下:
访问修饰符 class 类名 { //类成员 }
下面通过一个小例子来具体介绍下类及其成员。
例5-1:类的声明及其类成员(ConsoleClass)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleClass { class Program { static void Main(string[] args) { Student s = new Student(); s.name = "点点"; s.age = 26; s.sex = "女"; s.ShowMsg(); } } public class Student { public string name; public int age; public string sex; public void ShowMsg() { string str = "学生姓名:" + name + " 年龄:" + age + " 性别:" + sex; Console.WriteLine(str); Console.ReadLine(); } } }
运行结果如图5-1所示。
图5-1 类的声明及其类成员
此例中,声明了一个public的名为Student的类,Student类中定义了name、age、sex三个成员变量以及ShowMsg方法。在Main方法中实例化Student类的一个对象s,并使用“.”操作符调用类的成员变量为其赋值,之后调用ShowMsg方法输出信息。
2.方法
“方法”是包含一系列语句的代码块。程序通过“调用”方法并指定所需的任何方法参数来执行语句。在C#中,每个执行指令都是在方法的上下文中完成的。
方法在类或结构中声明,声明时需要指定访问级别、返回值、方法名称以及任何方法参数。方法参数放在括号中,并用逗号隔开。空括号表示方法不需要参数。其语法如下:
访问修饰符 返回值类型 方法名(参数列表) { //方法体 }
在例5-1中,public void ShowMsg()声明了一个public(公有)的、void(无返回值)的、名为ShowMsg的无参方法。
下面再通过一个小例子来具体介绍下不同类型方法的声明及使用。
例5-2:方法的声明及使用(ConsoleFunction)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleFunction { class Program { static void Main(string[] args) { MyClass mc = new MyClass(); mc.Show1(); Console.WriteLine(mc.Show2()); Console.WriteLine(mc.Show3(26)); Console.WriteLine(mc.Show4(125, 521)); Console.ReadLine(); } } public class MyClass { public void Show1() { Console.WriteLine("我是无返回值无参方法Show1"); } public int Show2() { int n = 1; Console.WriteLine("我是有返回值无参方法Show2"); return n; } public string Show3(int age) { Console.WriteLine("我是有返回值有参方法Show3"); if (age >= 18) return "您已成年,符合标准"; else return "您尚未成年,暂时不符合标准"; } public int Show4(int num1, int num2) { Console.WriteLine("我是有返回值有参方法Show4"); return num1 + num2; } } }
运行结果如图5-2所示。
图5-2 方法的声明及使用
此例中,定义了一个名为MyClass的类,在MyClass类里分别声明了4个不同形式的方法,在Main方法中,初始化MyClass类,并调用其4个方法,输出相关信息。在此要注意的是,有返回值的方法,如Show2,要使用return关键字返回一个与方法返回值类型相匹配的变量或值,且有返回值的方法中有且仅有一个return语句被执行。表5-1中列出了常用的几种访问修饰符。
表5-1 常用的访问修饰符
5.1.4 构造函数和析构函数
在C#中,构造函数用来初始化对象,而析构函数则用来清理对象。下面就分别介绍下构造函数和析构函数。
1.构造函数
构造函数与类名相同,通常用来初始化对象。任何时候,只要创建类,就会调用它的构造函数。一个类可能有多个接受不同参数的构造函数。构造函数使得程序员可设置默认值、限制实例化以及编写灵活且便于阅读的代码。若不显式地提供构造函数,则默认情况下C#将创建一个默认的构造函数,该构造函数实例化对象,并将成员变量设置为默认值表中列出的默认值。
例5-3的代码演示了声明类的构造函数并为成员变量初始化。
例5-3:构造函数的声明及其类成员的初始化(ConsoleClassConstructor)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleClassConstructor { class Program { public double chang = 7; public double width = 3; public double area; public Program() { area = chang * width; } public Program(double l, double w) { area = l * w; } static void Main(string[] args) { Program p1 = new Program(); Console.WriteLine("此长方形的面积为:" + p1.area); Program p2 = new Program(9, 4); Console.WriteLine("此长方形的面积为:" + p2.area); Console.ReadLine(); } } }
运行结果如图5-3所示。
图5-3 构造函数的声明及其类成员的初始化
此例中,分别声明了一个不带参数的构造函数以及一个带有两个double类型参数的构造函数,在构造函数中都是执行对变量area的赋值。不同点是默认的构造函数使用成员变量chang和width的乘积来为area赋值,而带参数的构造函数则使用两个参数的值的乘积为其赋值。在Main方法中,分别使用了两个不同的构造函数初始化了两个Program类的对象p1和p2,之后分别输出其下的area变量的值。
2.析构函数
析构函数用于析构类的实例。析构函数与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数以类名加~来命名。
例5-4的代码演示了声明类的析构函数的例子。
例5-4:析构函数的声明和使用(ConsoleClassDestructor)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleClassDestructor { class Program { ~Program() { Console.WriteLine("自动调用析构函数成功!"); Console.ReadLine(); } static void Main(string[] args) { Program p = new Program(); } } }
运行结果如图5-4所示。
图5-4 析构函数的声明和使用
此例中要注意的是,析构函数是自动被调用的,且一个类中只能有一个析构函数存在。
5.1.5 this关键字
在面向对象的编程语言中,this关键字用来引用类的当前实例,通过this关键字可以引用当前类的成员变量和方法。例5-5中演示了this关键字的用法。
例5-5:this关键字的用法(ConsoleClassThis)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleClassThis { class Program { private string time; public void SetMessage(string time) { this.time = time; } static void Main(string[] args) { Program p = new Program(); p.SetMessage("四点"); Console.WriteLine("会议通知:今天下午{0}开会...", p.time); Console.ReadLine(); } } }
运行结果如图5-5所示。
图5-5 this关键字的用法
此例中,定义了一个private的变量time,在SetMessage方法的参数中也定义了一个变量time,这时如果不使用this关键字,那么在方法体中time = time,此时的两个time就会认为是方法参数中的变量,而无法识别成private的成员变量。
5.1.6 属性
属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称为“访问器”的特殊方法。这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。
1.属性的声明及使用
首先以一个小例子演示下属性的声明及使用。
例5-6:属性的声明及使用(ConsoleProp)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleProp { class Program { static void Main(string[] args) { Calculate calc = new Calculate(); calc.Dividend = 10; calc.Divisor = 3; calc.Divided(); calc.Dividend = 10; calc.Divisor = 0; calc.Divided(); Console.ReadLine(); } } public class Calculate { private double dividend; private double divisor; public bool flag = false; public double Dividend { get { return dividend; } set { dividend = value; } } public double Divisor { get { return divisor; } set { if (value == 0) flag = true; else divisor = value; } } public void Divided() { if (flag) Console.WriteLine("除数不能为0"); else Console.WriteLine("计算结果为:" + Dividend / Divisor); } } }
运行结果如图5-6所示。
图5-6 属性的声明及使用
在此例中,声明了一个名为Calculate的类,Calculate类中声明了两个私有的变量dividend和divisor,分别作为被除数和除数,并为其分别添加了属性。在属性的定义中,使用了get/set方法(或访问器)。方法中使用value关键字作为要传入或取出的值。在Main方法中,初始化Calculate类,并为其两个属性赋值,为其赋值时会自动调用属性定义中的set方法,取值则调用get方法,之后调用Divided方法显示出计算结果。由于除数不能为0,例5-6在Divisor属性定义中使用了一个bool值的变量作为依据。Divided方法中根据bool值输出相应结果。
使用VS2012编程时,在C# 3.0以上版本中提供更简洁的属性的写法,如例5-6中代码:
private double dividend; public double Dividend { get { return dividend; } set { dividend = value; } }
可替换为
public double Dividend { get; set; }
属性的数据类型必须与它所访问的字段类型一致。属性的类型可以是整型、字符串或者是类等。
2.属性的访问类型
属性的访问类型分为以下三种。
(1)只读属性,只包含get方法。
(2)只写属性,只包含set方法。
(3)读写属性,包含get和set方法。
在此处要注意的是,C# 3.0自动生成的属性的语法必须同时指定get和set,而不能只写其中一个。那么使用自动生成的属性实现只读或只写属性要怎么做呢?方法很简单,如以下代码所示。
public string Username { get; private set; }
以上为只读属性,只写属性则把private修饰符写在get前面即可。