【唐老狮】Unity系列之C#四部曲—C#核心

面向对象三大特性
面向对象七大原则
面向对象相关知识
------------------------------------------------------------------------------
面向对象基本概念
把相关数据和方法组织为一个整体来看待,更贴近事物的自然运行模式
用程序来抽象对象
类:class 关键字
三大特性:封装 + 继承 + 多态
七大原则:开闭原则、依赖倒转原则、里氏替换原则、单一职责原则、接口隔离原则、合成复用原则、迪米特法则
------------------------------------------------------------------------------
类和对象
一般在namespace语句块中
class 类名 { 成员变量; 成员方法; 成员属性;构造函数; 析构函数;索引器;运算符重载;静态成员 }
class前可以加访问修饰符
- 同一个语句块中的不同类,不能重名
- 用帕斯卡命名法命名
类的申明和类对象(变量)申明是两个概念
类的申明类似枚举和结构体的申明,类的申明相当于申明了一个自定义变量类型
类名 变量名;
类名 变量名 = null;
类名 变量名 = new 类名();
Person p; // 仅在栈中开辟一个空间储存p
Person p2 = null; //同上
Person p3 = new Person(); //不仅在栈中开辟一个空间,同时在堆中也开辟了空间
------------------------------------------------------------------------------
类和对象练习题
(自己随便举个例子,视频中用类写的代码太麻烦了)
int[] array1 = new int[] { 1, 2, 3 };
int[] array2 = array1;
array2[0] = 99;
Console.WriteLine(array1[0]); // 输出: 99
在这个例子中,array1
是一个数组,它包含了三个整数。array2
是 array1
的一个新的引用,所以它们都指向同一个数组。
然后我们修改了 array2[0]
的值。由于 array2
和 array1
指向的是同一个数组,所以当我们通过 array1[0]
访问数组的第一个元素时,我们会看到已经被修改过的值。
int[] array1 = new int[] { 1, 2, 3 };
int[] array2 = array1;
array2 = null;
Console.WriteLine(array1[0]); // 输出: 1
在这个例子中,即使我们把 array2
设置为 null
,array1
仍然可以正常地访问原来的数组。这是因为 array2
和 array1
是两个独立的引用,改变 array2
的指向并不会影响到 array1
。
int[] array1 = new int[] { 1, 2, 3 };
int[] array2 = array1;
array2 = new int[] { 4, 2, 3 };
Console.WriteLine(array1[0]); // 输出: 1
Console.WriteLine(array2[0]); // 输出: 4
你可以看到,尽管我们改变了 array2
的值,但是 array1
仍然保持不变,它仍然指向原来的数组。这是因为 array2
和 array1
是两个独立的引用,改变 array2
的指向并不会影响到 array1
。
------------------------------------------------------------------------------
成员变量和访问修饰符
- 申明在类语句块中
- 用来描述对象特征
- 可以是任意变量类型
- 是否赋值根据需求来定
- 默认都是private
如果要在类中申明一个和自己相同类型的成员变量时不能对它进行初始化
--------------------------
- public 公共的,自己(内部)和外人(外部)都能访问和使用
- private 私有的,自己(内部)才能访问和使用,默认
- protected 保护的,自己(内部)和子类才能访问和使用
--------------------------
1.值类型的初始值:
- 数字类型:0
- 布尔值:false
- char:空字符
2. 引用类型:null
查看默认值的代码:default(数据类型)
------------------------------------------------------------------------------
成员方法
- 函数,用来表现对象行为;
- 申明在类语句块中,规则和函数相同,受到访问修饰符的影响;
- 成员方法不加static关键字;
- 受到访问修饰符影响
- 成员方法必须实例化出对象,再通过对象来使用,相当于该对象执行了某个行为;
------------------------------------------------------------------------------
构造函数和析构函数
在实例化对象时,会调用的用语初始化的函数
如果不写,默认存在一个无参构造函数
写法:
- 没有返回值
- 函数名和类名相同
- 没有特殊需求时,一般都是public的
- 构造函数可以被重载
- this代表当前调用该函数的对象自己
----
但结构体不允许
----
如果不自己实现无参构造函数,而实现了有参构造函数,会失去默认的无参构造
--------------------------
通过this重用构造函数代码
格式:
- 调用无参的重构函数:public 类名(参数): this() { 代码块 }; // 注意this前面的冒号
- 调用其他有参的重构函数:public 类名(参数): this( 参数 ) { 代码块 };
小trick:
=================
一个代码示例:
public class Rectangle
{
private int width;
private int height;
// 默认构造函数
public Rectangle() : this(1, 1)
{
}
// 参数构造函数
public Rectangle(int width, int height)
{
this.width = width;
this.height = height;
}
// 只有一个参数的构造函数
public Rectangle(int side) : this(side, side)
{
}
public void Display()
{
Console.WriteLine("Width: {0}, Height: {1}", width, height);
}
}
在这个例子中,我们有一个Rectangle
类,它有三个构造函数:
- 一个没有参数的构造函数,它通过
: this(1, 1)
调用另一个构造函数,将宽度和高度都设置为1。 - 一个接受两个参数的构造函数,它直接设置宽度和高度。
- 一个只接受一个参数的构造函数,它通过
: this(side, side)
调用另一个构造函数,将宽度和高度都设置为同一个值。
这样,无论你使用哪个构造函数创建Rectangle
对象,宽度和高度都会被正确地初始化。例如:
Rectangle rect1 = new Rectangle();
rect1.Display(); // 输出 "Width: 1, Height: 1"
Rectangle rect2 = new Rectangle(3, 4);
rect2.Display(); // 输出 "Width: 3, Height: 4"
Rectangle rect3 = new Rectangle(5);
rect3.Display(); // 输出 "Width: 5, Height: 5"
================
当引用类型的堆内存被回收时,会调用该函数
对于需要手动管理内存的语言,比如C++,需要在析构函数中做一些内存回收处理;但c#存在自动垃圾回收机制GC
--------------------------
建议反复观看,重点
分代算法
三级内存
主动垃圾回收
------------------------------------------------------------------------------
成员属性
用于保护成员变量
为成员属性的获取和赋值添加逻辑处理
解决public、private、protect的权限问题
属性可以让成员变量在外部只能获取不能修改或者只能修改不能获取
语法:
================
访问修饰符 属性类型 属性名
{
get
{
//可以返回之前添加i一些逻辑规则
//意味着这个属性可以获取的内容
return
}
set
{
//可以再设置之前添加一些逻辑规则
//value 关键字 用于表示外部传入的值
name = value;
}
}
================
--------------------------
get/set前可以加访问修饰符
默认不加,会使用属性申明时的访问权限
加的访问修饰符要低于属性的访问权限
不能让get和set的访问权限都低于属性的权限
--------------------------
一般是只有get(只能得),没有set(不能改)
--------------------------
作用:外部能得不能改得特征
如果类中有一个特征时只希望外部能得不能改的,又没什么特殊处理
那么可以直接使用自动属性
================
访问修饰符 属性类型 属性名
{
get; // 没有大括号
set;
================
------------------------------------------------------------------------------
索引器
让对象可以像数组一样通过索引访问其中元素,使程序看起来更直观、更容易编写
访问修饰符 返回值 this[参数理性 参数名,参数类型 参数名 ... ... ]
{
内部的写法和规则和索引器相同
get{}
set{}
}
仍旧不关注返回值,只有参数不同的区分
让我们可以以中括号的形式范围自定义类中的元素,规则自己顶,访问时和数组一样
比较适用于 在类中有数组变量时使用,可以方便的访问和进行逻辑处理
注意:
结构体里面也支持索引器
------------------------------------------------------------------------------
静态成员
关键字 static
用static修饰的成员变量、方法、属性
称为静态成员
特点:直接用类名点出使用
--------------------------
--------------------------
--------------------------
--------------------------
静态成员具有唯一性
--------------------------
成员变量只能将对象实例化出来后,才能点出来使用,不能无中生有
--------------------------
因为非静态成员和静态成员的生命周期有明显差异
--------------------------
常用唯一变了的申明
方便别人获取对象申明
静态方法:
常用的唯一的方法申明,比如 相同规则的数学计算相关函数
静态内存不会被垃圾回收,一般用在全局共享数据上
--------------------------
常量可以理解为特殊的static
常量必须初始化,不能修改
const只能修饰变量的
const一定是写在访问修饰符后面的
--------------------------
------------------------------------------------------------------------------
静态类和静态构造函数
用static修饰的类
特点:
- 只能包含静态成员
- 不能被实例化
作用:
- 将常用的静态成员写在静态类中,方便使用
- 静态类不能被实例化,更能体现工具类的唯一性
- 第17课:静态类和静态构造函数 P12 - 04:40
- 静态类和普通类都可以有
- 不能使用访问修饰符
- 不能有参数
- 只会自动调用一次
------------------------------------------------------------------------------
拓展方法
为现有的非静态 变量类型添加 新方法
如果拓展方法和原类中的方法重名,那么被原类中的方法覆盖
------------------------------------------------------------------------------
运算符重载
让自定义类和结构体能够使用运算符
关键字:operator
特点:
- 一定是公共的静态方法
- 返回值写在operator前
- 逻辑处理自定义
------------------------------------------------------------------------------
内部类和分布类
把一个类分成几部分申明
------------------------------------------------------------------------------
继承的基本概念
子类只能有一个父类(c#是单继承)
子类可以间接继承父类的父类
父类的private修饰的成员子类都不能使用
所以用protected修饰(外部不允许访问,但子类能用)
------------------------------------------------------------------------------
里氏替换原则
任何父类出现的地方,子类都可以替代
重点:父类容器能装子类对象
作用:方便进行对象的存储和管理
父类 变量名 = new 子类
注意:不能用子类容器去装父类对象
------------------------------------------------------------------------------
继承中的构造函数
注意:有参构造会顶掉默认的无参构造,如果想保留无参构造需要重载出来
当申明一个子类对象时,先执行父类的构造函数,再执行子类的构造函数
!
------------------------------------------------------------------------------
万物之父和装箱拆箱
object时所有类型的基类,它是一个类(引用类型)
- 可以用里氏替换原则,用object容器装所有对象
- 可以用来表示不确定类型,作为函数参数类型
用object存值类型(装箱)
把object转为值类型(拆箱)
------------------------------------------------------------------------------
密封类
使用sealed密封关键字修饰的类,让类无法再被继承
不允许最底层子类被继承
------------------------------------------------------------------------------
多态-Vob
让继承同一父类的子类们,在执行相同方法时有不同的表现(状态)
主要目的:
同一父类的对象,执行相同的行为(方法)有不同的表现
- 编译时多态:函数重载,开始就写好的
- 运行时多态:vob、抽象函数、接口
vob:virtual(虚函数)、override(重写)、base(父类)
虚函数:用来给子类重写
------------------------------------------------------------------------------
抽象类和抽象方法
被抽象关键字abstract修饰的类
- 不能被实例化的类
- 可以包含抽象方法
- 继承抽象类必须重写其抽象方法
- 只能在抽象类中申明
- 没有方法体
- 不能是私有的
- 继承后必须实现,用override重写
虚方法有方法体,但抽象方法没有;
虚方法可以选择性实现,但抽象方法必须在子类中实现
抽象方法必须写在抽象类中,虚方法没有限制
抽象类和抽象方法主要用于整体框架设计时
------------------------------------------------------------------------------
接口
接口是行为的抽象规范
它也是一种自定义类型,关键字:interface
申明规范:
- 不包含成员变量
- 只包含方法、属性、索引器、事件
- 成员不能被实现
- 成员可以不用写访问修饰符,不能是私有的
- 接口不能继承类,但可以继承另一个接口
使用规范:
- 类可以继承多个接口
- 类继承接口后,必须事先接口中所有成员
特点:
- 它和类的申明类似
- 接口是用来继承的
- 接口不能被实例化,但是可以作为容器存储对象
--------------------------
interface 接口名{}
接口是抽象行为的基类
接口命名规范:帕斯卡前面加个I
--------------------------
--------------------------
--------------------------
当一个类继承两个接口,但是接口中存在同名方法时
显式实现接口时,不能写访问修饰符
--------------------------
------------------------------------------------------------------------------
密封方法
用密封关键字sealed修饰的重写函数
让虚方法和抽象方法之后不能再被重写
特点:和override一起出现
------------------------------------------------------------------------------
命名空间
using 空间
或者 空间名.变量
不同namespace中允许有同名类
命名空间中的类默认为internal
------------------------------------------------------------------------------
万物之父的方法
------------------------------------------------------------------------------
String
本质上是一个密封类
字符串本质就是char数组,可以通过数组的形式来访问
可以转化为char数组
有length属性,表述string长度
Format函数
Remove函数
str的修改方法都会返回一个新的字符串
Replace函数
Substring方法
重点
Split函数,以某种符号作为分隔符,切分字符串
------------------------------------------------------------------------------
StringBuilder
用于处理字符串的公共类
修改字符串而不创建新的对象,需要频繁修改和拼接的字符串可以使用它,可以提升性能
using System. Text
自动扩容
获得容量:Capacity
获得字符长度:Length
------------------------------------------------------------------------------
结构体和类的区别
- 存储位置不同,一个在栈上,一个在堆上
- 结构体具有封装的特性,但不具备继承和多态的特性,因此大大减少了它的使用频率
- 由于结构体不具备继承的特性,所以它不能够使用protected保护访问修饰符
--------------------------
结构体可以继承 接口,因为接口是行为的抽象
--------------------------
对象是数据集合时,优先考虑结构体,比如位置、坐标等等
从值类型和引用类型赋值时的区别上考虑
------------------------------------------------------------------------------
抽象类和接口的区别
------------------------------------------------------------------------------
总结