类与对象
---恢复内容开始---
一、概述
面向对象编程(Object Oriented Programming,简称:OOP)是现在应用非常广泛的一种计算机编程思想,它指在现实世界中的事物或概念通过抽象的方法模拟到计算机程序中,它将现实世界中的所有事物视为某个对象,这种编程的方式允许通过程序员在编程时自定义这些对象,同时定义可这些对象进行操作的方法。
二、类与对象(操作工具Visual Studio2013)
面向对象程序设计中使用类的概念,一个描述一种类型的事物,比如:图书、猫、狗等。而对象是这种事物中具体的一个,比如《C#编程基础》这本书,小明家的狗等,都是属于对象。OOP思想是编程中的难点之一,下面我们更加深入的去了解类与对象的含义。
1、对象:在整个OOP架构中对象是一个很基本但是又很难描述的清晰概念,它可以是一个真实存在的实体,如一辆车,或者是一条🐩(狗)等,它也可以是一个很抽象的存在,比如数据库的操作对象等。
对于一个初学者来说,完全可以抛开那些让人眼花缭乱的概念,见得的理解对象就是用来说明某个物体的,既然要说明某个物体,那我们就会有几个问题:“它是什么(有什么特征)、它能做什么等”
在程序中,一个对象是由三个最基本的要素组成的:属性、方法、和事件
我们先来了解一下属性和方法:
属性:属性是用来告诉我们这个对象是什么,也就是描述和反馈对象的特征。例如说:一辆车,它的颜色、品牌、类型则是这些属性的值。通过这些属性的值我们就可以想象出这一辆车是什么样的。属性一般使用短小的意义明确的的名词来标注,这样能让用户很明白它的含义。
方法:方法是用来告诉我们对象能做什么,也就是对象的行为。比如一部可以打电话、发消息、拍照、上网的手机,在这里,打电话、发消息、拍照、上网都是手机所具有的方法,看到这些手机的方法我们就知道这个手机能够用来完成哪些工作了。方法一般用意义明确的动词来标注。
2、类:一个对象可能有很多个属性,例如说:一辆车,它的颜色、品牌、类型、长、款、高等几十个属性具体到程序中我们就需要用几十个变量来保存这些信息。如果一个程序中有多个汽车对象怎么办呢?难道真的要定义成百上千个变量??这样你可能会疯。
现在我们换一个角度来考虑这些问题,把一个汽车的信息放到一个表格中,如下所示:
品牌 | 价格 | 尺寸 | 类型 | 进口/国产 | 生产厂商 |
RAV4 | 20万 | 4630*1815*1685 | 四挡手动 | 国产 | 一汽丰田 |
福克斯 | 10万 | 4640*1515*1985 | 五档手动 | 国产 | 长安福特 |
骊威 | 8万 | 4630*1815*1685 | 五档手动 | 国产 | 东非日产 |
骏捷 | 9万 | 4670*1825*1785 | 四挡手动 | 国产 | 华晨中华 |
奔驰 | 100万 | 4830*1835*1695 | 7档手动 | 进口 | 奔驰 |
从上面的表格中我们可以发现,汽车都是可以用品牌、价格、尺寸、类型、等名词来进行说明,事实上这些就是汽车的属性,既然这样我们是否可以用一个统一的结构来定义所有的汽车对象呢??在C#中,类就可以帮我们完成这些工作。
在OOP中类被称作为对象的模板使用,也就是说类是创建对象的模板,就像盖房子所用到的设计图一样。每栋房子都包含很多设计上的要求,同样的每个对象也都包含了很多的数据,也提供了处理和访问这些数据的方法,房屋设计图纸表明了施工过程中的各种标准和要求,同样的,类规定了它的每个对象(也称为实例)可以包含什么数据功能。例如我们定义一个汽车的类,那么每个具体的汽车就可以看做这个类的一个实例。我们还可以在类中定义属性,这样实例就可以通过这些属性赋值来保存具体的值,也可以在类中定义汽车所具有的各种方法,这样实例就可以通过调用这些方法来完成具体的操作。
类的定义语法如下:
[访问修饰符] class 类名 { 类的成员 }
其中,class是创建类的所用关键字。类名的命名一般使用帕斯卡命名法,即首字母大写、非首字母小写,如果有多个单词则每个单词的首字母都要大写,如:Person,Book,UserInfo
访问修饰符用于描述的可访问性,对于类来说,访问修饰符是可选的。常见的访问修饰符如下表:
访问修饰符 | 说明 |
public | 公有访问、不受任何限制 |
private | 私有访问。只限于本类成员访问 |
internal | 内部访问。只限于本项目访问 |
对于类来说,默认的访问修饰符是internal,通常可以使用public
下面根据语法来定义一个汽车类
using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { class Program { //定义一个汽车类 public class Car { } static void Main(string[] args) { } } }
类的成员:
定义在类内部的元素都是类的成员,类的成员主要包括字段、属性、构造函数、方法等。
下面我们来了解一下字段、属性和构造函数
1、字段:字段用于存储类中的数据,可以理解成类中的变量。比如人的姓名、年龄、身高、性别等特征,那么描述人的类就应该有存储姓名、年龄、身高、性别等信息的字段。
声明字段的语法如下
[访问修饰符] 数据类型 字段名称
访问修饰符通常使用private,意味着在类中才能直接访问字段。数据类型就是int,char,float,double,string,bool等,字段名跟变量命名规则相同,建议使用驼峰命名法进行命名。在有需要的时候,字段可以指定初始值。
定义一个描述人的类:并添加姓名、年龄、身高、性别的字段,性别默认为“男”
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { //定义一个描述人类的类 public class Person { //字段成员 private string name; //姓名 private int age; //年龄 private float height; //身高 private string gender = "男"; //性别,默认初始值为“男” } }
2、属性:对于字段来说,通常是private来修饰,这样只能在类的内部直接使用字段。但是实际中,我们经常会想在其它地方使用类及类成员,那么久必须保证类成员能被用到。对于private的字段来说,我们需要通过属性吧它公开出去,这样在类的外部才能达到间接使用字段的目的。
属性不表示具体的存储位置,它仅用于封装字段,给字段提供对外的访问接口。定义属性的语法如下:
[访问修饰符] 数据类型 属性名称 { get{return 字段} set{字段=value} }
属性用于公开私有字段,所访问的修饰符采用public以方便用户使用。数据类型一般情况下该属性操作的字段保持一致。属性的名称和字段类似,但一般采用帕斯卡命名法。
属性体中的get和set都是属性的访问器,get称为读访问器,通过这个访问器,外部用户可以读取字段,因此在get部分我们需要使用return关键字段数据反馈给用户。set称为写访问器,通过这个访问器,外部用户可以为字段赋值,用户输入的值就存放在value关键字中,这是一个系统定义好的关键字,我们可以直接使用它。
属性get和set访问器可以同时具备,也可以只有一个,一次划分,属性可以分成三种,可读可写的属性(get和set都有)、只读属性(只有get)和只写属性(只有set)。
下面我们给上面的Person类的字段添加属性,选中字段成员,右键选择重构,再选择封装字段即可。也可以直接用快捷方式,Ctrl+R,Ctrl+E
可以直接看图操作:
代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { //定义一个描述人类的类 public class Person { //字段成员 private string name; //姓名 //姓名对应的属性 public string Name { get { return name; } set { name = value; } } private int age; //年龄 //年龄对应的属性 public int Age { get { return age; } set { age = value; } } private float height; //身高 //身高对应的属性 public float Height { get { return height; } set { height = value; } } private string gender = "男"; //性别,默认初始值为“男” //性别对应的属性 public string Gender { get { return gender; } set { gender = value; } } } }
经过这样的修改后,我们就将所有的字段公开来了,访问了属性就相当于访问了字段
我们也可以通过属性中的set访问器对用户输入的信息进项相关的验证,现在我们将上面Person类中的年龄要求范围定义在18-25岁之间,超出范围则将值默认为18,
代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { //定义一个描述人类的类 public class Person { //字段成员 private string name; //姓名 //姓名对应的属性 public string Name { get { return name; } set { name = value; } } private int age; //年龄 //年龄对应的属性 public int Age { get { return age; } set { if (value >= 18 && value <= 25) { age = value; } else { age = 18; } } } private float height; //身高 //身高对应的属性 public float Height { get { return height; } set { height = value; } } private string gender = "男"; //性别,默认初始值为“男” //性别对应的属性 public string Gender { get { return gender; } set { gender = value; } } } }
自动属性:
public class Person { //自动属性 public string Name{get;set;} public int Age{get;set;} public float Height{get;set;} public string Gender{get;set} }
使用自动属性的时候不需要定义字段,get和set也不需要像以前一样复杂,但是这种属性结构是无法实现输入验证,而且定义的时候必须同时给出get和set两个访问器
3、构造函数
构造函数在类中用于创造类对象,顾名思义,就是构造出一个具体的实例函数。构造函数是类中特殊的一个成员函数,当类没有构造函数时,系统会自动为该类创建一个默认的无参的构造函数:一个类中可以定义多个构造函数,不过当定义了构造函数之后,系统不会自动创建默认构造函数了。构造函数的语法如下:
[访问修饰符] 构造函数名(参数列表)
{
构造函数的主体;
}
对此语法需要注意以下几点:
1、访问修饰符通常使用public
2、构造函数名称必须和类名相同
3、参数列表是可选的:定义参数跟定义的变量的语法基本相同;多个参数用逗号隔开
4、构造函数的主体部分代码一般是为了初始化类的字段
下面给Person类添加构造函数,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { //定义一个描述人类的类 public class Person { //字段成员 private string name; //姓名 //姓名对应的属性 public string Name { get { return name; } set { name = value; } } private int age; //年龄 //年龄对应的属性 public int Age { get { return age; } set { if (value >= 18 && value <= 25) { age = value; } else { age = 18; } } } private float height; //身高 //身高对应的属性 public float Height { get { return height; } set { height = value; } } private string gender = "男"; //性别,默认初始值为“男” //性别对应的属性 public string Gender { get { return gender; } set { gender = value; } } //构造函数 public Person() //无参的构造函数 { } public Person(string pName)//带一个参数的构造函数 { this.name = pName;//初始化name字段 } public Person(string pName,int pAge)//带两个参数的构造函数 { this.name = pName;//初始化name字段 this.age = pAge;//初始化age字段 } } }
此类中添加了3个构造函数,他们的区别在于参数不同,我们可以这样理解,当后面创建一个具体的Person类对象时,无论是否知道他的姓名和年龄,或者只是知道姓名,或者姓名和年龄都知道,我们都可以有对应的构造函数创建出此对象来。构造函数的主体,主要是给本类字段或属性初始化的语句;this关键字的作用是调用本类中的成员,this.name就是本类中name的字段、当然,不用this也是可以的。
4、对象
类其实只是一个模板,在使用的时候,一般需要将类进行实例化,从而得到我们想要的对象。对象就是类的实例。就好像房屋设计图纸不能住人,要根据图纸盖出的房子才能真正的住人一样。将类进行实例化成一个对象的过程需要借助new关键字完成。语法如下:
类名 对象名=new 类名([参数列表]);
此语法格式总,对象名自定义,相当于变量名一样,参数列表是可选的,取决于使用的类的构造函数。另外,类中的实例属性成员的访问是通过类对象来完成的(访问属性其实就访问了字段),语法如下:
对象名.属性=值; //赋值 变量=对象名.属性 //取值
下面我们在Program类的Main()函数中将Person类进行实例化并访问属性,代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OOP { class Program { static void Main(string[] args) { Person p1 = new Person(); //调用无参数的构造函数进行实例化 p1.Name = "张三"; //给Name属性赋值 p1.Age = 18; //给Age属性赋值 Console.WriteLine("学生{0}的年龄是{1}岁",p1.Name,p1.Age); //取出属性的值 Person p2 = new Person("李四"); //调用一个带一个参数的构造函数进行实例化 p2.Age = 20; //给Age属性赋值 Console.WriteLine("学生{0}的年龄是{1}岁", p2.Name, p2.Age); //取出属性的值 Person p3 = new Person("王五",22); //调用一个带两个个参数的构造函数进行实例化 Console.WriteLine("学生{0}的年龄是{1}岁", p3.Name, p3.Age); //取出属性的值 Console.ReadKey(); } } }
运行结果如下:
在此程序中,Person p1 = new Person()调用无参构造函数时,并没有初始化p1对象的具体信息,后面才通过对Name和Age属性的赋值来存储p1的对象的数据。由此看来,如果有很多个属性,这样的赋值方式会比较麻烦,所以这也体现了带参数的构造函数的优势,就比如最后的Person p3 = new Person("王五",22) 一句话就完成了对象的创建和初始化的工作。在实际中,我们就可以根据这样的需要来添加类的构造函数。
另外,有了对象之后,为什么不能访问属性而不直接访问字段?这个是因为字段是private的,在另外的类中是不能访问到的:而属性是public的,才能在另外的地方访问。值得注意的是,要能对属性进行赋值要求属性必须带set访问器,要能读取属性的值要求属性必须带get访问器。
总结
在这里主要学习了类和对象的定义和使用,在这里,重点需要理解类与对象的概念以及他们之前的关系,即类是对象的抽象,对象是类的具体化,在此基础上,掌握类及类中的字段,属性,构造函数的定义,以及根据类来声明相应的对象。
---恢复内容结束---