上一节(教学思路 c#之面向对象二)初步理解面向对象的基本概念中,我没有提到任何的代码,只是用语言和实例来说明什么是类和对象以及面向对象的特性等基本概念,类是c#程序语言的重要核心,也是构建应用程序最主要的元素,在.net类库中,提供了大量支持创建各种应用程序功能的相关类,类是一个庞大的内容,我要分成两节来讲解,本节课我要教同学们如何声明类、对象以及定义类成员,针对类本身的组成与对象关系,进行详细的说明,下节课我们会深入的学习类。
首先让我们回忆一下上节课说过的一些概念,什么是类?什么是对象呢?类就是具有相同的属性和功能的对象的抽象的集合,而对象呢?就是这个类中的一个实体,属于一个类的对象,必须包含共同的特征,而对象之间又因为各自的特性和行为的不同区分。用我听过的一个笑话来解释一下上面我说过的话,比如说人类是一个类,那么一个身高1米的孩子,和2米24的姚明站在一起,我们一定会说都是人,孩子和姚明就是人这个类的两个对象,我们不能说,因为孩子身高太矮,就把他说成是猴子,身高、姓名、性别、篮球打得怎么样等特性就是区别这两个对象的要素。
这是现实生活中的实例,我现在用个程序用的实例来说明一下类和对象,在面向对象的领域里,类是用来封装应用程序的功能和逻辑的。比如我们以后会学习的一章文件、目录的操作中,将使用Dir类,Dir是目录的意思,那么Dir类就是对目录进行的管理和维护的操作类,也就是说,只要对目录进行操作,都应该使用Dir类的一个对象,让这个对象来做你想要做的工作,比如让一个dir类的对象创建一个目录,再用这个对象来删除目录,同时我们还可以创建多个Dir类的对象,来做工作,这时我们就需要考虑,如果是对同一个目录进行操作,创建过多的对象会造成资源上的浪费,所以同学们在以后的编程中要记得尽可能的简化程序。
我们在编程过程中.Net类库会提供很多实现功能的类,比如刚才提到的Dir类,还有我们经常用到负责数据输出到控制台所使用的Console类,我们可以直接用,但是如果我们需要自己定义类时,比如将来我们要做一个企业网站,在实现后台操作时,我们可以把对新闻的增加、删除、修改、查找都定义在一个News类中,把对图片的操作定义在另一个Pictures类中比如限制上传图片的类型、大小等,这样当你想对新闻做操作时,就使用News类的一个对象来工作,如果想对图片做操作,就用Pictures类的一个对象来做工作,日后如果想修改维护网站时,你想修改新闻功能,你只需要修改News类即可,不会影响图片的功能,这就是类的一个好处,也就是面向对象的一个特性:封装性。通过这两个实例,我们基本上应该理解了类和对象的概念,下面我们来学习如何创建一个类,同时声明一个类的实例,也就是类的一个对象。
定义类
定义类的基本语法形式为:([]内的可以省略,如省略代表修饰符即为默认值)
[访问修饰符/类修饰符] Class ClassName [:Class-base]
{
Class -body;
}
其中[类修饰符]中包括访问修饰符和new、abstract、sealed这三种,[访问修饰符] 我们上节课学习过,是用来界定这个类允许以何种级别访问,其他这三种我们在本节课也将学习到,接着看类的定义,紧跟修饰符的是Class关键字和一个用来命名该类的标识符,[:Class-base]定义此类所继承的基类或实现的接口,这部分我们也会在以后学习到,这里不做过多的解释,大家只要知道,如果省略的话,代表这个类没有除了继承了Object类外,没有其他父类或接口,这里提到了Object类,Object类是所以类的父类,这里涉及到了C#底层设计,比较难理解,也与我们实际开发暂无太大联系,可以不考虑。接下来的“{}”中封装了类的主体,Class -body中包含类中所有的相关成员,它可以包括数据成员(字段、常量)、函数成员(方法、属性、事件、索引器、运算符、实例构造函数、析构函数和静态构造函数)和嵌套类。这些成员我们依次都会学到,先来说说嵌套类,它只的意思是类中还可以有类,大家先这样理解就可以了,具体嵌套的类有哪些特性,我们一会也会讲到。
下来看看下面的代码实例,我们来定义一个类军人Armyman类,类中有3中成员,常量、字段、方法。
类的声明
1 //定义一个军人类,军人类的标识符为Armyman,默认缺省访问修饰符为private,除了Object类外,没有继承其他类
2 class Armyman
3 {
4 //定义了一个军人国籍的常量为中国,访问修饰符是公有的。
5 public const string country = "中国";
6 //定义了一个军人姓名的字段,访问修饰符是公有的。
7 public string ArmymanName="";
8 //定义了一个军种字段,我们将要在后面的实例中将这个字段和下面的方法变成属性
9 string Armytype="";
10 //定义了一个军种功能的方法,传递了军种的参数到方法内部,通过对军种的不同的判断,输出不同的功能。下面的判断最好使用swicth语句!
11 public void ArmyFunction(string Armytype)
12 {
13 if (Armytype == "陆军")
14 Console.WriteLine("现代陆军都是大规模的机械化部队,拥有大量的重装备和后勤补给。");
15 if (Armytype == "海军")
16 Console.WriteLine("杜鲁门曾说过:海军到哪里,国家的权利就到哪里 .");
17 if (Armytype == "空军")
18 Console.WriteLine("战略空军将使战争形态向可控战争转变。");
19 else
20 Console.WriteLine("{0}不是兵种,请输入现代战争中的三大兵种!海军、陆军、空军",Armytype");
21 }
22 }
解释一下上面的这段代码类成员,首先定义了一个常量,常量的定义是使用const关键字,而且定义的同时必须赋值。常量就是在程序中永远不能改变的数据,必须通过类的名字进行引用,如果我们定义了一个常量,并且赋给常量一个值,那么想改变这个常量的值时,编译器便会报错。其他两种成员通过注释已经很清楚了,在这里我就不解释了,我们看一下访问修饰符,当一个成员没有加修饰符时,默认为private,即私有的成员。上节课我们学到当一个成员定义为private时,只有类的内部成员可以使用它,我们在这个例子中有一个成员没有加public修饰符 ,目的就是为了下面的实例在创建对象的时候 ,体会到如果是private成员,定义后,在这个类的外部是无法访问的,而public成员可以访问到。类的定义我们就先说到这,接下来我们来看类实例及成员引用。
类实例及成员引用
当我们创建完一个类,如果要使用这个类,必须在程序里创建类的实例对象,通过这个实例来引用类中所定义的方法成员,完成所需的工作。创建实例对象时,必须使用new关键字,这个关键字我们在集合和哈希表对象的创建时用到过,基本方法一致,就是用new关键字产生一个指向类实例对象所在位置的引用。实例化类对象的基本语法为:
ClassName ObjName=new ClassName([参数]);
类的名字 对象名 是否有参数、多少个参数由类的构造函数决定,现在我们都使用无参数来创建对象
new关键字用来创建真正的ClassName类的实例对象,并返回这个对象的引用,“=”运算符则将此对象的引用(在托管堆中的地址)指定给ObjName变量,也就是说ObjName中存放的指向托管堆中ClassName对象的地址,这个ObjName存放在线程堆栈中,当这段程序执行后,通过ObjName这个对象名称,便可以直接引用ClassName类所定义的成员。
我们现在来紧跟着上个例子创建一个军人类Armyman 的实例对象ASoldiers(士兵):
Armyman ASoldiers=new Armyman();
创建完成,如果想让这个士兵完成一些Armyman类中功能,就要使用运算符(.),比如ASoldiers.ArmymanName就可以给士兵起名字如图1,这个过程就叫做引用。
下面的实例我们来具体完成一下类的创建和声明对象以及对象的引用。
定义类、声明对象,引用类成员
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace testcsharp
6{
7 //定义一个军人类,军人类的标识符为Armyman,除了Object类外,没有继承其他类
8 class Armyman
9 {
10 //定义了一个军人国籍的常量为中国,访问修饰符是公有的。
11 public const string country = "中国";
12
13 //定义了一个军人姓名的字段,访问修饰符是公有的。
14 public string ArmymanName = "";
15
16 //定义了一个军种字段,我们将要后面的实例中将这个字段和下面的方法变成属性
17 public string Armytype = "";
18
19 //定义了一个军种功能的方法,传递了军种的参数到方法内部,通过对军种的不同的判断,输出不同的功能。
20 public void ArmyFunction(string Armytype)
21 {
22 switch (Armytype)
23 {
24 case "陆军":
25 Console.WriteLine("现代陆军都是大规模的机械化部队,拥有大量的重装备和后勤补给。");
26 break;
27 case "海军":
28 Console.WriteLine("杜鲁门曾说过:海军到哪里,国家的权利就到哪里 .");
29 break;
30 case "空军":
31 Console.WriteLine("战略空军将使战争形态向可控战争转变。");
32 break;
33 default :
34 Console.WriteLine("{0}不是兵种,请输入现代战争中的三大兵种!海军、陆军、空军", Armytype);
35 break;
36
37 }
38 }
39 /**//* 我们补充定义了一个静态static的军种功能的方法,
40 * 和上面的方法不同的是,无参、并且多了static,
41 * 相同的是方法的标识符相同都是ArmyFunction,这就构成了方法的重载
42 * 另外使用了static关键字,使得这个方法不能通过对象来引用,而必须通过类名来引用*/
43 public static void ArmyFunction()
44 {
45 Console.WriteLine("无论什么军种,作用都是:保卫国家人民、捍卫国家尊严。");
46 }
47 }
48 class Program
49 {
50 //Main方法是程序的入口点,所以无论这个Program类放到哪里,程序都会从Main方法开始执行。
51 static void Main(string[] args)
52 {
53 //我们来创建一个军人类Armyman 的实例对象ASoldiers(士兵)
54 Armyman ASoldiers = new Armyman();
55
56 //我们来定义一个string类型的变量soldiercountry,接受国籍,并且把它打印出来。
57 //因为国籍country是常量,必须通过类名进行引用,所以使用Armyman . 也就是类名.。
58 //有因为在Armyman类中,常量country的修饰符是public,所以在Armyman类的外部是可以访问到的
59 string soldiercountry = Armyman.country;
60 Console.WriteLine("士兵的国籍是:"+soldiercountry );
61 Console.WriteLine();
62
63 //我们来给这个士兵起个名字,叫“许三多”,因为ArmymanName军人姓名在Armyman类中是public的字段,
64 //在Armyman类的外部可以访问到所以通过对象名.,来引用军人姓名。
65 ASoldiers.ArmymanName = "许三多";
66
67
68 //下面我们定义兵种,如果按照上一个例子中的第九行,Armytype的修饰符是默认缺省的private,
69 //现在我们可以试一下,是用对象名点不出来的,也就是无法访问到。
70 //图1就是Armytype的修饰符为private时,输入对象名ASoldiers和“.”后的截图,我们看是没有Armytype这个成员供对象引用的。
71 //为了讲解属性的作用,我们来把Armyman类中的Armytype字段改成public公有的如第,现在Armytype成员就可以点出来。
72 //我们给Armytype赋个"男性"这个值,并且打印出来。
73 ASoldiers.Armytype = "男性";
74 Console.WriteLine("士兵{0}的兵种是{1}",ASoldiers .ArmymanName,ASoldiers.Armytype);
75 Console.WriteLine();
76
77 /**//*很明显我们发现了对于Armytype类中的Armytype(兵种)字段,存在了一个漏洞,就是男性不是兵种的一种。
78 * 这时我们可以通过下面的方法弥补一下,也就是当我们输入兵种后,将兵种作为参数带到Armyman的ArmyFunction方法中进行判断,再输出兵种的功能。*/
79 ASoldiers.ArmyFunction(ASoldiers.Armytype);
80 Console.WriteLine();
81
82 ASoldiers.Armytype = "陆军";
83 Console.WriteLine("士兵{0}的兵种是{1}", ASoldiers.ArmymanName, ASoldiers.Armytype);
84 ASoldiers.ArmyFunction(ASoldiers.Armytype);
85 Console.WriteLine();
86 //这样就能阻止一部分的错误,但是这还不是最合理的,下节课我们将要教授同学们属性的使用,完全解决了这个问题。
87
88 //下面我们再来使用一下static修饰的ArmyFunction的重构方法,
89 //这个重构的方法因为是static静态的,所以需要用类名引用
90 Armyman.ArmyFunction();
91
92 }
93 }
94
95}
运行结果如下:
我们通过实例学习了声明类、创建类实例对象,以及类成员因为修饰符的部分,访问权限和引用方式也是不同,这里做个总结,当成员修饰符是public时,在第二方Program类中才能被访问到,如果类成员是常量const或是static静态的,需要用类名来引用,其他的类成员都是用对象名来引用。下面说一个static关键字,被static关键字修饰的类成员叫做静态成员,相对的没用static关键字修饰的统称为实例成员,被static关键字修饰的类叫做静态类,那么类还有哪些类型呢?除了静态类(含static)、实例类(不含static)还有两种类,分别是:抽象类和密封类,我会在系列教案思路的继承一中具体讲解类的分类的内容。下一节我们来继续上一个例子中提到的改善军种的功能,学习类二:属性。