C#学习笔记——面向对象(贰)
一、类和对象
1、类的概念
具有相同特征,相同行为的一类事物的抽象
2、类的声明
访问修饰符 class 类名{
特征——成员变量
行为——成员方法
保护特征——成员属性
构造函数和析构函数
索引器
运算符重载
静态成员
}
3、注意事项
- 类声明和类对象(变量)声明是两个概念
- 类的申明 类似 枚举和结构体申明,相当于申明了一个自定义变量类型,对象是由类创建出来的,创建对象的过程称为实例化对象,类对象是引用类型
4、类对象声明
类名 变量名;
类名 变量名 = null;
类名 变量名 = new 类名();
二、访问修饰符
访问修饰符:3P
public 公共(内外部都可以访问和使用)
private 私有(只有内部才能访问和使用,不写默认为private)
protected 保护 (自己内部和子类才能访问和使用)
三、成员变量和成员方法
1、成员变量使用
对象.变量
- 默认值:类中变量都会有默认初始值。查看默认值:
Console.WriteLine(default(变量类型))
2、成员方法
特点:
- 不加static关键字
- 要先实例化对象再通过对象使用
- 受访问修饰符影响
四、构造函数和析构函数
1、构造函数
-
概念:实例化对象时,会调用的一个用于初始化的函数,不写默认存在无参构造函数,无返回值,函数名和类名相同
-
注意
- 类中允许自己声明无参构造函数,结构体中则不允许
- 构造函数可以被重载
- this代表当前调用该函数的对象自己
- 若有有参构造函数,则默认无参构造函数会失去
-
特殊写法:
通过this重用构造函数代码,先调用this中的构造函数,再调用自己的,若是无参构造函数的this,则this的参数中填常量
访问修饰符 构造函数名(参数列表) : this(参数1,参数2...)
2、析构函数
-
概念:当引用类型的堆内存被回收时,会调用该函数(垃圾真正被回收时才调用),对于需要手动管理内存的语言(如c++),就需要析构函数处理,但c#中有自动垃圾回收机制GC,所以基本不用,只做了解
-
用法
~构造函数名()
{
}
五、成员属性
1、基本概念
-
用于保护成员变量
-
为成员属性的获取和赋值添加逻辑处理
-
解决3P的局限性
-
属性可以让成员变量在外部 只能获取不能修改 或 只能修改不能获取
2、基本语法
访问修饰符 属性类型 属性名
{
get{}
set{}
}
3、使用
p.Name = "唐老狮"
可进行加密和解密
4、get和set
-
get和set前可加访问修饰符
-
默认不加 会使用属性声明时的访问权限
-
加的访问修饰符要低于属性的访问权限
-
不能让get和set的访问权限都低于属性的权限(里面不能比外面高级,其中public是高级的,private是低级的)
-
-
get和set可以只有其中1个
5、自动属性
如果没有什么特殊处理的需要可以使用,它允许快速声明属性而不需要手动定义字段和getter/setter方法:
public float Height{
get;
set;
}
等价于:
private float height;
public float Height{
get{
return height;
}
set{
height = value;
}
}
六、索引器
1、基本概念
让对象可以向数组一样通过索引访问其中的元素,是程序看起来更直观,更易编程。可以重载
2、语法
访问修饰符 返回类型 this[参数类型 参数名,参数类型 参数名...]
{
get{}
set{}
}
3、使用
public Person this[int index]{
get{
return friends[index];
}
set{
friends[index] = value;
}
}
Person p = new Person();
p[0] = new Person();
4、用处
适用于在类中有数组变量时使用,可以方便访问和逻辑处理
七、静态成员
1、基本概念
static修饰的成员变量,方法,属性,可以直接用类名点出来使用
2、自定义静态成员和方法
public static float PI = 3.1415926f;
public static float CalcCircle(float r) { }
3、静态成员的使用
类名.成员
类名.方法()
4、static特点
-
程序开始运行时就会分配内存空间,静态成员与程序同生共死
-
静态函数中不能使用非静态成员,非静态函数中可以使用静态成员(生命周期的差异性)
5、常量和静态变量
const可理解为特殊的static
共同点:都可以通过类名点出来使用
不同点:
- const必须初始化并且不能修改,static没有这个限制
- const只能修饰变量,而static可以修饰变量、方法等
- const必须写在访问修饰符后面,static没有这个限制
八、静态类和静态构造函数
1、静态类
-
static修饰的类
-
只能包含静态成员
-
无法实例化,可以体现工具类的唯一性
-
将常用的静态成员写在静态类中,方便使用
2、静态构造函数
- static修饰的构造函数
- 静态类和普通类都可以有
- 无法使用访问修饰符,不能有参数
- 静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。 静态构造函数最多调用一次。
九、拓展方法
1、概念
拓展方法可以为现有的非静态变量类型添加新方法
2、作用
- 提升程序拓展性
- 无需在对象中重写方法
- 无需继承来添加新方法
- 可以为别人封装的类型额外添加新的方法
3、语法
访问修饰符 static 返回值 函数名(this 拓展类名 参数名, 参数类型 参数名, 参数类型 参数名...)
- 与静态函数相比只是参数不同(第一个参数)
- 必须写在静态类中,必须是静态函数
- 第一个参数为拓展的对象,用
this修饰
4、实例
为int拓展一个SpeakValue方法
static class Tools
{
public static void SpeakValue(this int value){
Console.WriteLine("int拓展方法:" + value);
}
}
int i = 10;
i.SpeakValue();
十、运算符重载
1、概念
让自定义类和结构体能够使用运算符
2、特点
-
一定是公共的静态方法
-
返回值写在operator前
-
逻辑处理自定义
3、注意事项
-
关系运算符需要成对实现(如> 和 <必须一起重载)
-
一个符号可以多个重载
-
不能使用ref和out
4、语法
public static 返回类型 operator 运算符(参数列表)
十一、内部类和分部类
1、内部类
-
概念:在一个类中再申明一个类
-
特点:使用时要包裹者点出自己
-
作用:亲密关系的变现
-
注意:访问修饰符作用很大(即在类前加修饰符)
2、分布类
-
概念:把一个类分成几部分申明
-
关键字:partial
-
作用:分布描述一个类,增加程序的拓展性
-
注意:
- 分布类可以写在多个脚本文件中
- 分布类中访问修饰符要一致
- 分布类中不能有重复成员
十二、继承
1、继承的概念
类A继承类B,类A会继承类B的所有成员,拥有类B的所有特征和行为。同时子类还可以拥有自己的特征和行为
其中类B称为父类、基类、超类;类A称为子类、派生类
2、特点
- 单根性:子类只能有一个父类
- 传递性:子类可以间接继承父类的父类
3、语法
class 类名 :被继承类名
{
}
十三、里氏替换原则
1、概念
面向对象七大原则最重要的原则,即任何父类出现的地方,子类都可以替代。
语法表现:父类容器装子类,因为子类对象包含了父类的所有内容。
2、is和as
- is: 判断一个对象是否是指定类对象,返回值为bool
- as: 将一个对象转换为指定类对象,返回值为转换后的对象,失败则返回null
3、使用
父类装载子类,方便存储和管理
若父类没有子类的方法,则可以使用is判断和as转换,将对象转换成子类,进而调用子类的方法
例: GameObject是Player父类
GameObject player = new Player();
if (player is Player){
(player as Player).PlayerAtk();
}
十四、继承的构造函数
1、概念
- 父类的无参构造函数很重要,子类实例化时,默认调用父类的无参构造。
- 子类继承时,想要通过子类的构造函数去调用父类的构造函数,可以通过
base关键字。 - 继承中的构造函数的执行顺序: ... -> 父类的父类的构造 -> 父类构造 -> 子类构造
2、语法
class Father{
public Father(int i){
...
}
}
class Son : Father{
public Son(int i) : base(i){
...
}
}
十五、密封类
1、概念
密封类是用sealed密封关键字修饰的类
2、作用
- 让类无法再被继承
- 在面向对象程序设计中,密封类主要作用是不允许最底层子类被继承
十六、多态
1、多态的概念
继承同一父类的子类们,执行相同方法时有不同的表现
2、实现
vob: 虚函数virtual、重写override、父类base
class Father{
public virtual void Atk(){
// 父类攻击行为
}
}
class Son : Father{
public override void Atk(){
base.Atk(); // 调用父类的行为
// 子类的攻击行为
}
}
十七、抽象类和抽象方法
1、抽象类
- 被
abstract修饰的类 - 抽象类中所有封装相关的知识点都能用
- 可在抽象类中写抽象函数
- 遵循里氏替换原则
abstract class Person{}
2、抽象函数
-
不能有函数体,只有定义
-
不能是私有的
-
继承抽象类后子类必须用override重写抽象函数
public abstract void Walk();
3、抽象方法和虚方法
- 区别
- abstract方法只能在抽象类里实现,而virtual则任意
- virtual方法有函数体,可以选择是否写逻辑
- virtual方法可以由子类选择是否实现
4、使用
- 不希望被实例化,相对比较抽象的类可以使用抽象类
- 父类的行为不太需要被实现,只需子类去具体定义
- 整体框架设计时会使用
十八、接口
1、概念
接口是行为的抽象规范,也是一种自定义类型。
接口声明规范:
- 不包含成员变量,只包含方法、属性、索引器、时间
- 成员在接口中不能被实现,可以不写访问修饰符(默认public),但不能是私有的
- 接口无法继承类,但可以继承另一个接口
接口使用规范:
- 类可以继承多个接口
- 类继承接口后,必须实现接口中所有成员
2、声明
interface 接口名
{
...
}
3、使用
class 类名 : 父类, 接口1, 接口2...
4、接口同名
当一个类继承多个接口,但是接口中存在同名方法时,需要显示实现接口,实现时,不能写访问修饰符
实现语法:
接口名.行为名
实例:
public class Player : IAtk,ISuperAtk
{
void IAtk.Atk(){
...
}
void ISuperAtk.Atk(){
...
}
}
使用语法:
转成对应接口再使用
(p as IAtk).Atk();
(p as ISuperAtk).Atk();
十九、密封方法
1、概念
密封方法是用 sealed 密封关键字修饰的 重写函数
2、作用
让虚方法或者抽象方法之后不能在被重写(如让子类不能重写)
3、特点
和override一起出现
4、实例
class WhitePerson : Person{
public sealed override void Eat(){
base.Eat();
...
}
}
二十、命名空间
1、概念
命名空间是用来组织和重用代码的
2、语法
namespace 命名空间名
{
}
3、使用
不同命名空间中相互使用,需要引用命名空间或指明出处
// 1、引用命名空间
using MyGame
GameObject obj;
// 2、指明出处
MyGame.GameObject obj;
4、特点
- 不同命名空间中允许有同名类,使用时只能用指明出处的方法
- 命名空间可以包裹命名空间
5、关于修饰类的访问修饰符
-
public - 公共的
-
internal - 只能在该程序集中使用,命名空间默认为internal
-
abstract - 抽象类
-
sealed - 密封类
-
partial - 分部类