我们说过,学习完变量赋值分支循环,理论上我们就可以干任何事情了。从函数开始,我们就从代码实现 步入了代码管理 的领域。几千上万行的时候,我们可以用几十个函数实现对代码的组织重用,但随着代码量的进一步增加,代码行数以万计数以十万级,函数都数以千计数以万计的时候,我们怎么办呢?
光是记忆这些函数,人脑都相当吃力了!(其实还有命名,^_^)
注意,牢记这一点,人脑的局限或者特点,是我们不得不求助于
面向对象
的重要原因。
想一想,我们如何管理成千上万的函数?一个简单的办法,就是分
类:
比如,和控制台相关的函数一类,和计算相关的函数一类……这样,我们想调用一个控制台方法的时候,就可以先找到控制台(类),然后在控制台中寻找方法。事实上,我们也确实是这样做的:
Console
Console就是一个类,点(.)就相当于“的”,WrtieLine()就是方法名。所以整行代码的意思就是:调用Console类的WrtieLine()方法。
演示:F12转到WrtieLine()
我们也可以自己声明(创建)一个类,这需要使用
class关键字
在控制台项目右键->Add->New Class,或直接使用Shift+Alt+C,就会弹出添加类(文件)的窗口,输入类名,比如Student(命名规则/规范),点击Add,就会在项目中添加一个Student.cs文件,文件中已经有如下代码:
using
演示:
先说
.cs文件
我们也把它称之为类文件,cs是csharp的缩写。通常,一个 .cs 文件里面放一个class,文件名和类名相同。但是,.cs文件名也可以和类名不一致,一个.cs文件里也可以有多个class,为了教学演示方便,我们前期就会这样做。本质上,.cs的文件名不影响类名,文件名和class冲突,以class后面定义的类名为准。
另外,类还可以被放置在类中,形成类中类:
class
或者使用partial关键字生成部分类:
partial
(同名的)部分类会在编译的时候被合成为一个类,所以我们完全可以把部分类当成一个类使用。
但这些都是非常规的做法,我们一般不要使用。尽量一个类一个类文件。
对类文件而言,.cs后缀很关键,不能修改,它指示编译器使用C#语法进行编译。如果代码以http://VB.NET书写,就以.vb为后缀。
.cs文件的内容实际上分为三部分:using、namespace、class。这涉及到
类的名称
实际上,关键字class后面跟的不是类的全名,而是一个部分名 。类的全名是:名称空间+点(.)+部分名,类的名称空间由namespace定义,所以这个类的全名是Yuanzhan.Student。为什么搞得这么复杂呢?
首先,类名是不能重复的。所以,当类越来越多,重名的几率就会越来越大,“命名”就会变得越来越困难。怎么办?一个办法就是加前缀。;但是,“就比如“学生”,这简直太太普遍了源栈的学生”(YuanZhan.Student),就好多了;如果还担心源栈有重名,就再加一个“大飞哥的源栈的学生”(Dafeige.Yuanzhan.Student)就是了。部分名之前的前缀就叫做
名称空间
注意名称空间一般都是按层级组织的,比如:
- Dafeige.Yuanzhan.Student
- Dafeige.Yuanzhan.Teacher
- Dafeige.17bang.User
- Dafeige.17bang.Problem
- ……
但这就是为了便于(开发人员)理解和记忆。C#编译器不关心名称空间的“层级”,换言之,Dafeige和Dafeige.17bang是两个完全不同的名称空间。
然而,每次都使用全名,会非常的麻烦!所以,C#提供了这样一个机制:当源代码中出现了部分名时(也就是找不到这个类名的时候),编译器会依次将部分名:
- 和当前代码所在的namespace组合,
- 和using中的namespace组合(using后面也只能跟名称空间)
生成全名,以确定这个类。
所以,我们来看下面的代码:
- Student实际上是Yuanzh.Student(和当前所在的名称空间组合)
- Console实际上是System.Console(和using中的System组合)
有时候还会出现部分类名可以和当前类文件中using的多个名称空间组合的情形,比如有两个User类:
//命名不能以数字开头,所以加一个下划线
在使用的时候就会报错:
编译器无法知道你究竟是要_17bang.User还是要feige.User,这时候你只能使用全名:
new
有时候全名会很长,这时候可以使用名称空间别名:
using
现在回头看看我们一直在使用的Program.cs,它也是一个类文件,其中的class就声明了的Program类,Program中又声明了Main()方法。注意Main()方法前面的
static(静态)
它表示这个方法:
- 属于这个类
- 可以直接由类名进行调用
我们可以把之前和学生相关的方法都搬到Student类中去:
class
这样,greet()就变成Student的方法了,我们可以在Main()中通过Student调用:
static
你可能会问:为什么以前就可以不用Student.呢?哈哈,因为以前你的greet方法是属于Program类的呀!同一个类中的方法之间的互相调用是不需要加类名的。
你还一定注意到了我们在greet()方法前面加了一个public关键字。它被称之为:
访问修饰符
它决定了greet()方法能不能在类的外部被调用(访问),包括:
- public:公开的,可以在外部任何地方使用
- internal:内部的,可以在当前项目中使用
- private(默认):私有的,只能在自己类的内部使用
我们可以在类中再装入两个方法,使用不同的访问修饰符:
internal
演示:
新建一个项目OutSample,引用YuanZhan项目,在OutSample的Main()中调用Student.greet()等……
访问修饰符除了可以修饰方法(以及后文讲到的其他类成员),也可以修饰类本身。但类不能使用private,且默认修饰符是internal。
通常来说,
- public的成员命名,我们使用命名法;
- private的成员命名,我们使用(小)驼峰命名,甚至在前面加上下划线(_);
- internal的成员命名,可以匈牙利可以驼峰,本教材我们统一使用驼峰命名法
访问修饰符对于面向对象至关重要,因为依靠它,才能真正的实现:
封装
把方法分门别类的组织起来,装到不同的类里面,这只做到了装,还没有做到封。只有使用了这些访问修饰符,才能保证让类能够封闭一些逻辑(代码实现),开放一些接口(调用方法)。
为什么需要封闭呢?开放不好么?现在不都提倡“开源”么……
咳咳!这个不是这样理解的。鉴于同学们目前的开发经验,我只能要求大家记牢两点:
- 总是尽可能地给最低的访问权限
- 不要把访问修饰符当成“安全策略”使用(通过反射,即使private的类成员都可以被获取)
类里面除了可以存放方法,还应该可以存储数据。因为对数据的封装是一个非常普遍的要求(参照之前的元组)。在类中存放数据的成员叫做:
字段
注意不要将字段和变量混淆,在类中声明的是字段,在方法中声明的是变量:
class
他们还有以下区别:
- (静态)字段可以在类中任何地方声明,在任何地方使用;变量在方法中声明,作用域为自声明开始到声明位置对应的第一个花括号(})结束
- 如果字段和变量同名,在同一个方法中,变量优先
- 字段被属于类,通过类调用;变量不属于任何成员,直接使用
- 变量未赋值不能使用;字段可以使用,值为默认值
static
你说等等,还有字段前要加static,变量前不能加static……
停!错!!!变量前面确实不能加static,但字段也是可以不使用static:
internal
不仅字段,方法也可以没有static哟!他们被称之为
实例
你注意到没有,我们本来是要讲面向对象的,但我们一直都在讲类啊!对象在哪里?
对象是类的实例,所以,实例化一个类就能得到这个类对象。
我擦!什么鬼?
稍安勿躁,^_^,我们有了类的概念,比如学生类,学生类里可以放一些和学生相关的方法或字段。但有些东西是类的静态成员无法体现的,比如学生的姓名:
Student
学生这个类的姓名都叫“阿泰”?这说不通。我们只能说某一个学生的姓名叫“阿泰”,另一个学生的姓名叫“陈元”,这样才对。当然,阿泰和陈元都有共同的特点,他们都在飞哥的源栈学习(learn()),都有学习成绩(score),等等,所以他们是学生。
在面向对象的世界里,阿泰和陈元都是对象,而且是Student类的对象,因为他们都具有(能调用)Student类所定义的,但是有各自独立的方法(行为)和字段(状态)。
那如何获得呢?new一个就可以了:
new
这样我们就得到了一个Student类型的对象。使用这个对象,就可以调用Student类中所有实例 方法和字段:
//可以调用实例成员
因为对象通过类创建,我们也可以认为:类是创建对象的模板。
但是注意运行上述代码,我们会发现代码行2的结果是0而不是23。这是因为new是一个运算符,意味着“构建”,每new一次都会生成一个新的对象。所以代码行1和代码2实际上各自生成了两个不相关的对象,新生成的对象上的age字段没有被赋值,所以只有其默认值0。
所以我们通常会在通过new得到一个对象之后,将其赋值给一个同类型的变量:
Student
只有通过wx变量指向这个对象,就可以反复的使用了:
wx
延伸阅读:
当面试官面试你时,提问谈谈你对面向对象编程的理解时该怎样回答?
作业:
- 观察“一起帮”的:
- 注册/登录功能,定义一个User类,包含字段:Name(用户名)、Password(密码)和 邀请人(InvitedBy),和方法:Register()、Login()
- 求助版块,定义一个类Problem,包含字段:标题(Title)、正文(Body)、悬赏(Reward)、发布时间(PublishDateTime)和作者(Author),和方法Publish()
- 帮帮币版块,定义一个类HelpMoney,表示一行帮帮币交易数据,包含你认为应该包含的字段和方法
- 为这些类的字段和方法设置合适的访问修饰符。
每日单词:
感谢童鞋们的阅读!^_^
我就是:黑律师/包工头/创业狗/老码农……现在还是教书匠的大飞哥。
再次重申这个系列的目标是:
1)通俗易懂。2)实战为主。3)面向就业。
系列内容的完善需要你的反馈!
欢迎点赞和评论,以及加入我们的QQ交流群:326801052。