# 类和对象

所谓面向对象是指在编程时将任何事物都看成是一个对象来描述。

对象包括属性和方法，属性是指对象固有的特征，方法则是对象的行为。

面向对象语言的三大特征分别是封装、继承、多态。

## 类

类定义的具体语法形式如下。

```C#
类的访问修饰符    修饰符    类名
{
    类的成员
}
```

其中：

- 类的访问修饰符：用于设定对类的访问限制，包括 public、internal 或者不写，用 internal 或者不写时代表只能在当前项目中访问类；public 则代表可以在任何项目中访问类。
- 修饰符：修饰符是对类本身特点的描述，包括 abstract、sealed 和 static。abstract 是抽象的意思，使用它修饰符的类不能被实例化；sealed 修饰的类是密封类，不能 被继承；static 修饰的类是静态类，不能被实例化。
- 类名：类名用于描述类的功能，因此在定义类名时最好是具有实际意义，这样方便用户理解类中描述的内容。在同一个命名空间下类名必须是唯一的。
- 类的成员：在类中能定义的元素，主要包括字段、属性、方法。

## 访问修饰符

在 C# 语言中类定义后就要确定类中包含的内容，类中包含的内容被称为类中的成员。

类中的成员包括字段、属性、方法。每个类成员在定义时需要指定访问修饰符、修饰符。

类的访问修饰符主要有两个，即 internal 和 public,如果省略了访问修饰符，即为 internal。

1. public
   成员可以被任何代码访问。
2. private
   成员仅能被同一个类中的代码访问，如果在类成员前未使用任何访问修饰 符，则默认为 private。
3. internal
   成员仅能被同一个项目中的代码访问。
4. protected
   成员只能由类或派生类中的代码访问。

在修饰字段时通常用两个修饰符，即 readonly （只读）和 static （静态的）。

使用 readonly 修饰字段意味着只能读取该字段的值而不能给字段赋值。

使用 static 修饰的字段是静态字段，可以直接通过类名访问该字段。

需要注意的是常量不能使用 static 修饰符修饰。

```C#
访问修饰符    修饰符    数据类型    字段名；
```

## 方法的定义

方法是将完成同一功能的内容放到一起，方便书写和调用的一种方式，也体现了面向对象语言中封装的特性。

```C#
访问修饰符    修饰符    返回值类型    方法名(参数列表)
{
    语句块;
}
```

其中：

1. 访问修饰符
   所有类成员访问修饰符都可以使用，如果省略访问修饰符，默认是 private。
2. 修饰符
   在定义方法时修饰符包括 virtual（虚拟的）、abstract（抽象的）、override（重写的）、static（静态的）、sealed（密封的）。override 是在类之间继承时使用的。
3. 返回值类型
   用于在调用方法后得到返回结果，返回值可以是任意的数据类型，如果指定了返回值类型，必须使用 return 关键字返回一个与之类型匹配的值。如果没有指定返回值类型，必须使用 void 关键字表示没有返回值。
4. 方法名
   对方法所实现功能的描述。方法名的命名是以 Pascal 命名法为规范的。
   5. 参数列表
      在方法中允许有 0 到多个参数，如果没有指定参数也要保留参数列表的小括号。参数的定义形式是“数据类型参数名”，如果使用多个参数，多个参数之间需要用逗号隔开。

## get 和 set 访问器

属性经常与字段连用，并提供了 get 访问器和 set 访问器，分别用于获取或设置字段的值。

get 访问器和 set 访问器的使用与方法非常类似，可以在操作字段时根据一些规则和条件来设置或获取字段的值。

此外，为了保证字段的安全性，还能选择省去 get 访问器或 set 访问器。

```C#
public    数据类型    属性名
{
    get
    {
        获取属性的语句块;
        return 值;
    }
    set
    {
        设置属性得到语句块;
    }
}
```

其中：

- get{}
  get 访问器，用于获取属性的值，需要在 get 语句最后使用 return 关键字返回一个与属性数据类型相兼容的值。

若在属性定义中省略了该访问器，则不能在其他类中获取私有类型的字段值，因此也称为只写属性。

- set{}
  set 访问器用于设置字段的值，这里需要使用一个特殊的值 value，它就是给字段赋的值。

在 set 访问器省略后无法在其他类中给字段赋值，因此也称为只读属性。

## 调用类成员

调用类的成员实际上使用的是类的对象，对于创建类的对象，首先可以将类理解成一个模板，类的对象则是按照这个模板定制的一个对象。

创建类对象的语法 形式如下。

```C#
类名对象名 = new 类名();
```

上面的语法形式是一种简单形式，通过“对象名”即可调用类中的成员。调用的语法形式如下。

```C#
对象名 . 类的成员
```

给属性赋值的代码如下。

```C#
类的对象 . 属性名 = 值;
```

使用类的对象调用方法的代码如下。

```C#
类的对象 . 方法名(参数);
```

如果将类中的成员使用修饰符 static 声明，则在访问类成员时直接使用“类名.类成员”的方式即可。

## 构造函数（构造方法）

创建类的对象是使用“类名 对象名 = new 类名()”的方式来实现的。

实际上，“类名()”的形式调用的是类的构造方法，也就是说构造方法的名字是与类的名称相同的。

构造方法的定义语法形式如下。

```C#
访问修饰符 类名 (参数列表)
{
语句块；
}
```

这里构造方法的访问修饰符通常是 public 类型的，这样在其他类中都可以创建该类的对象。

如果将访问修饰符设置成 private 类型的，则无法创建该类的对象。构造方法中的参数与其他方法一样，都是 0 到多个参数。

此外，构造方法是在创建类的对象时被调用的。通常会将一些对类中成员初始化的操作放到构造方法中去完成。

## 析构函数

构造方法是在创建类的对象时执行的，而析构方法则是在垃圾回收、释放资源时使用的。

析构方法的定义语法形式如下。

```C#
~类名()
{
    语句块；
}
```

在析构方法中不带任何参数，它实际上是保证在程序中会调用垃圾回收方法 Finalize()。

## 方法重载（函数重载）

定义构造方法时提到可以定义带 0 到多个参数的构造方法，但构造方法的名称必须是类名。实际上，这就是一个典型的方法重载，即方法名称相同、参数列表不同。

参数列表不同主要体现在参数个数或参数的数据类型不同。在调用重载的方法时系统是根据所传 递参数的不同判断调用的是哪个方法。

## 方法的参数（实参和形参）

方法中的参数分为实际参数和形式参数，实际参数被称为实参，是在调用方法时传递的参数；形式参数被称为形参，是在方法定义中所写的参数。

方法中的参数除了定义数据类型外，还可以定义引用参数和输出参数。引用参数使用 ref 关键字定义，输出参数使用 out 关键字定义。

引用参数在方法中使用时必须为其值，并且必须是由变量赋予的值，不能是常量或表达式。

如果需要将方法中的每一个参数都设置为 ref 类型参数，则需要在每一个参数前面加上 ref 关键字修饰。

引用参数与我们平时使用的参数有些类似，但输出参数不同，输出参数相当于返回值,即在方法调用完成后可以将返回的结果存放到输出参数中。

输出参数多用于一个方法需要返回多个值的情况。需要注意的是，在使用输出参数时，必须在方法调用完成前为输出参数赋值。

在调用含有带输出参数的方法时，必须在传递参数时使用 out 关键字，但不必给输出参数赋值。

## struct：结构体类型

结构体与类比较相似，由于它是值类型，在使用时会比使用类存取的速度更快，但灵活性方面没有类好。

结构体从字面上来理解是指定义一种结构，实际上结构体是一种与类的定义非常相似的数据类型，但它是值类型。

结构体的定义位置与枚举类型一样，都是在类中定义或者在命名空间下定义，而不能将其定义到方法中。

在结构体中能定义字段、属性、方法等成员。定义的语法形式如下。

```C#
访问修饰符  struct  结构体名称
{
    //结构体成员
}
```

其中：

1. 访问修饰符
   通常使用 public 或者省略不写，如果省略不写，代表使用 private 来修饰。
   如果结构体中的内容要被其他类中的成员访问，需要将其访问修饰符设置为 public。
2. 结构体名称
   命名规则通常和变量的命名规则相同，即从第二个单词开始每个单词的首字母大写。
3. 结构体成员
   包括字段、属性、方法以及后面要介绍的事件等。

在结构体中也能编写构造器，但必须带参数，并且必须为结构体中的字段赋初值。

在调用结构体的成员时，能使用不带参数的构造器，与创建类的对象时类似。

结构体与类有些类似，但其定义却有很大区别，具体如下表所示

| 结构体                                          | 类                                            |
| ----------------------------------------------- | --------------------------------------------- |
| 允许不使用 new 对其实例化                       | 必须使用 new 实例化                           |
| 没有默认构造方法                                | 有默认构造方法                                |
| 不能继承类                                      | 能继承类                                      |
| 没有析构方法                                    | 有析构方法                                    |
| 不允许使用 abstract、protected 以及 sealed 修饰 | 允许使用 abstract、protected 以及 sealed 修饰 |
