home commit with new plugins
This commit is contained in:
+123
-188
@@ -1,245 +1,180 @@
|
||||
# 构造函数:
|
||||
# 构造函数
|
||||
概念:
|
||||
实例化时,用于初始化的函数
|
||||
不写默认有一个无参构造函数
|
||||
==可以重载==
|
||||
- 构造函数用于在对象创建时做初始化
|
||||
- 构造函数名必须和类名相同
|
||||
- 构造函数没有返回值类型
|
||||
- ==可以重载==
|
||||
|
||||
通常用法:
|
||||
自定义初始化规则
|
||||
- 给字段或属性设置初始值
|
||||
- 保证对象一创建出来就处于可用状态
|
||||
|
||||
语法:
|
||||
访问修饰符 类名(){}
|
||||
```csharp
|
||||
访问修饰符 类名(参数列表)
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
案例:
|
||||
```Csharp
|
||||
class Person()
|
||||
```csharp
|
||||
class Person
|
||||
{
|
||||
public string name;
|
||||
public int age;
|
||||
public string Name;
|
||||
public int Age;
|
||||
|
||||
public Person()
|
||||
{
|
||||
name = "默认值";
|
||||
age = 默认值;
|
||||
Name = "默认值";
|
||||
Age = 0;
|
||||
}
|
||||
|
||||
public Person(string name, int age)
|
||||
{
|
||||
Name = name;
|
||||
Age = age;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
this关键字:
|
||||
this指当前类声明的成员变量
|
||||
```Csharp
|
||||
class Person()
|
||||
`this` 调用其他构造函数:
|
||||
```csharp
|
||||
class Person
|
||||
{
|
||||
public string Name;
|
||||
public int Age;
|
||||
|
||||
public Person() : this("默认值", 0)
|
||||
{
|
||||
public string name;
|
||||
public int age;
|
||||
public Person()
|
||||
{
|
||||
name = "默认值";
|
||||
age = 默认值;
|
||||
}
|
||||
public Person(int age,string name) //重构
|
||||
{
|
||||
this.name = name;
|
||||
//后面的name是传递的参数,this.name是最上方public int出来的
|
||||
this.age = name;
|
||||
}
|
||||
}
|
||||
|
||||
public Person(string name, int age)
|
||||
{
|
||||
Name = name;
|
||||
Age = age;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
特殊写法:
|
||||
通过this重用构造函数代码,即先调用无参构造函数,然后在调用这个特殊写法的构造函数
|
||||
语法:访问修饰符 构造函数名(参数列表):this(【可选】参数1,参数2.....)
|
||||
例子:
|
||||
```Csharp
|
||||
public Person(int age, string name):this(age)
|
||||
{
|
||||
balabala
|
||||
}
|
||||
```
|
||||
过程:先调用无参Person构造函数,然后寻找只有构造单个参数age的构造函数进行初始化
|
||||
|
||||
注意:
|
||||
==函数名和类名必须相同==
|
||||
==没有特殊用途,一般都是public==
|
||||
|
||||
==如果写了有参构造函数且自己没有保留无参构造函数,那么类在实例化时只能使用有参构造函数且无法使用无参构造函数==
|
||||
|
||||
|
||||
1. ==如果类里完全没写任何实例构造函数,编译器才会自动补一个无参构造函数==
|
||||
2. ==只要你自己写了任意实例构造函数,默认无参构造函数就不会再自动生成==
|
||||
3. 构造函数通常写 `public`,但也可以根据设计写成 `private` / `protected`
|
||||
4. 子类构造函数可以通过 `base(...)` 调用父类构造函数
|
||||
|
||||
----
|
||||
# 析构函数
|
||||
概念:
|
||||
当引用类型堆内存被回收时,会调用该函数
|
||||
对于需要手动管理内存的语言(C++),需要在析构函数中做一些内存回收处理
|
||||
C#有自动垃圾回收机制GC
|
||||
Unity中基本不用管
|
||||
|
||||
通常用法:
|
||||
|
||||
- C# 里的析构函数更准确地说是==终结器(finalizer)==
|
||||
- 当对象即将被 GC 回收时,运行时可能会调用它
|
||||
- 它主要用于“兜底清理非托管资源”
|
||||
|
||||
语法:
|
||||
```Csharp
|
||||
~类名()
|
||||
```csharp
|
||||
class Person
|
||||
{
|
||||
}
|
||||
```
|
||||
案例:
|
||||
```Csharp
|
||||
~Person()
|
||||
{
|
||||
|
||||
~Person()
|
||||
{
|
||||
// 清理代码
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
1. 析构函数只能写在==类==里,结构体不能写
|
||||
2. 不能手动直接调用析构函数
|
||||
3. 析构函数执行时机==不确定==
|
||||
4. 大多数业务代码里==不推荐为了练习而滥写析构函数==
|
||||
5. 现代 .NET 中,更常见的做法是优先使用 `IDisposable`、`using` 和 `SafeHandle`
|
||||
|
||||
----
|
||||
# 垃圾回收
|
||||
垃圾回收(GC)
|
||||
通过遍历堆Heap上动态分配的所有对象
|
||||
通过识别他们是否被引用来确定哪些对象时垃圾,哪些对象仍然需要引用
|
||||
垃圾:没有变量或对象引用的内容
|
||||
如果是垃圾就要被回收释放
|
||||
垃圾回收(GC):
|
||||
- GC 是 .NET 的自动内存管理机制
|
||||
- 它负责识别哪些托管对象还“活着”,哪些已经不可达,可以回收
|
||||
|
||||
一般都是==被动触发(会造成卡顿)==,所以游戏一般需要手动执行垃圾回收
|
||||
语法:GC.Collect();
|
||||
==GC会造成卡顿,所以在加载Loading时一般会调用==
|
||||
基础理解:
|
||||
1. 只要对象还能从“根”访问到,就一般不会被回收
|
||||
2. 访问不到的对象,才有机会被 GC 回收
|
||||
3. GC 主要管理的是==托管堆上的对象==
|
||||
|
||||
垃圾回收有多种算法
|
||||
引用计数
|
||||
标记清楚
|
||||
标记整理
|
||||
复制集合
|
||||
分代概念:
|
||||
- .NET GC 常分为 0、1、2 代
|
||||
- 新对象一般先进入 0 代
|
||||
- 活得越久,越可能被提升到更高代
|
||||
- 大对象通常会进入 LOH(大对象堆),阈值常见记法是 >= `85,000` 字节
|
||||
|
||||
注意:
|
||||
GC只负责Heap内存的垃圾祸首
|
||||
引用类型都是在Heap中的,所以他需要GC来进行管理
|
||||
1. ==`GC.Collect()` 一般不建议手动乱调==
|
||||
2. 手动触发 GC 可能带来性能抖动
|
||||
3. “值类型一定在栈上、引用类型一定在堆上”不是绝对规则
|
||||
4. GC 解决的是==托管内存==问题,不等于所有资源都会自动及时释放
|
||||
|
||||
栈Stack上的内存时系统自动管理的
|
||||
所以不需要GC
|
||||
----
|
||||
# GC 和 IDisposable
|
||||
首先区分资源类型:
|
||||
|
||||
C#中垃圾回收机制的大概原理
|
||||
他会把内存分为0,1,2代内存
|
||||
代的概念是垃圾回收中的一种算法需要
|
||||
新分配的对象都会被配置在第0代内存中
|
||||
每次分配都可能会进行垃圾回收(一般来说0代内存满了就会触发)
|
||||
在第一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行一下两部
|
||||
1.标记对象,从根(静态参数、方法参数)开始检查引用对象,标记后为可达对象,未标记就是不可大对象
|
||||
不可达对象就会认为是垃圾
|
||||
2.搬迁对象 压缩堆 (挂起执行内存托管代码线程:把第0代内存(还不是垃圾的内存)放到一代进行搬迁)释放未标记的对象、搬迁可达对象、修改引用地址
|
||||
- **托管资源**
|
||||
比如普通对象、字符串、集合。主要由 GC 管理。
|
||||
- **非托管资源**
|
||||
比如文件句柄、数据库连接、Socket、窗口句柄。需要更及时地手动释放。
|
||||
|
||||
大对象总被认为是二带内存,目的是减少性能损耗
|
||||
不会对大对象进行压缩搬迁 大对象是85000kb以上的
|
||||
为什么要用 `IDisposable`:
|
||||
- 因为 GC 只负责“对象内存”回收
|
||||
- 但像文件、连接、句柄这类资源,经常需要你主动关闭
|
||||
|
||||
示意图
|
||||
step1:程序放入内存
|
||||
![[Pasted image 20260412210327.png|697]]
|
||||
step2:发现沾满了
|
||||
![[Pasted image 20260412210704.png]]
|
||||
step3:原来的地址搬迁,无引用的垃圾丢掉
|
||||
![[Pasted image 20260412210810.png]]
|
||||
step4:D放入
|
||||
![[Pasted image 20260412210946.png]]
|
||||
step5:如果0代1代后续又满了,那么这套算法会同时做用在两代内存中,以此类推
|
||||
类似多级缓存cache,L1最快但常换L3最慢
|
||||
#基础
|
||||
# GC和IDispose()
|
||||
### 首先区分资源类型:
|
||||
|
||||
- **托管资源**:C# 自己管理的内存(比如普通对象、字符串)
|
||||
- 不用你管,**垃圾回收器(GC)会自动释放**
|
||||
- **非托管资源**:C# 管不了的外部资源
|
||||
- 例子:文件流、数据库连接、网络套接字、图片句柄、系统内核对象
|
||||
- **必须手动释放**,否则会造成**资源泄漏**(程序越跑越慢、卡死)
|
||||
|
||||
### 为什么要用 `IDisposable`?
|
||||
|
||||
- 非托管资源 GC 不会自动清理
|
||||
- 必须提供一个**标准、统一**的方式手动释放
|
||||
- `IDisposable` 就是 .NET 专门定义的**释放资源标准接口**
|
||||
|
||||
### 标准释放模式
|
||||
例子:
|
||||
最常见写法:
|
||||
```csharp
|
||||
using System;
|
||||
using var stream = new FileStream("demo.txt", FileMode.OpenOrCreate);
|
||||
// 使用 stream
|
||||
```
|
||||
|
||||
// 1. 实现 IDisposable 接口
|
||||
说明:
|
||||
- `using` 结束时会自动调用 `Dispose()`
|
||||
- 这比“等 GC 什么时候想起来再收”更可靠
|
||||
|
||||
标准释放模式示意:
|
||||
```csharp
|
||||
public class MyResource : IDisposable
|
||||
{
|
||||
// 标记资源是否已释放
|
||||
private bool _disposed = false;
|
||||
private bool _disposed;
|
||||
|
||||
// 2. 公共释放方法(外部调用)
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// 告诉GC:不用再管我了,我已经手动释放完了
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// 3. 核心释放逻辑
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// 释放 托管资源
|
||||
}
|
||||
if (disposing)
|
||||
{
|
||||
// 释放托管资源
|
||||
}
|
||||
|
||||
// 释放 非托管资源(比如关闭文件、关闭数据库连接)
|
||||
// 这里写你的资源释放代码
|
||||
// 释放非托管资源
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
// 4. 析构函数:防止用户忘记调用 Dispose
|
||||
~MyResource()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
~MyResource()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
```
|
||||
##### 解释:
|
||||
逻辑:
|
||||
1. 用完实例化对象
|
||||
2. 调用`.Dispose()`手写的`.Close()`等方法进行手动释放
|
||||
3. 判断标记变量
|
||||
4. 没释放就执行释放代码
|
||||
5. 标记为`true`
|
||||
|
||||
`GC.SuppressFinalize(this);` 的作用
|
||||
告诉.NET的GC这个对象的非托管资源,我**手动释放完了**,你不需要在垃圾回收的时候,再执行它的析构函数了。==它不会取消GC运行,只是取消执行析构函数!==
|
||||
|
||||
|
||||
|
||||
析构函数 `~MyResource()` 是干嘛的?和 GC 啥关系?
|
||||
防止你忘记调用 `Dispose()`
|
||||
场景模拟
|
||||
1. 你写了代码,**马虎忘了**调用 `.Dispose()`
|
||||
2. 非托管资源(文件 / 数据库)一直占着没释放
|
||||
3. 等到 GC(清洁工)来回收垃圾时
|
||||
4. **自动触发析构函数** `~MyResource()`
|
||||
5. 函数里执行 `Dispose(false)`,强行把非托管资源释放了
|
||||
|
||||
|
||||
==一个实例化对象dispose后就不会再用了(设计上就是如此,用过dispose后标签一直是true)==
|
||||
==而且由于它dispose了,内部引用是空的,所以只会留下一个实例化的小壳==
|
||||
==实例化的小壳也会因为没有被引用被GC清理,不会造成太大的性能问题==
|
||||
|
||||
如果需要==一次性==彻底释放
|
||||
例子:
|
||||
```Csharp
|
||||
var resource1 = new MyResource();
|
||||
resource1.Dispose();
|
||||
|
||||
//手动切断引用
|
||||
resource1 = null;
|
||||
//手动触发GC
|
||||
GC.Collection();
|
||||
GC.WaitForPendingFinalizers();//等待回收完成
|
||||
```
|
||||
补充理解:
|
||||
1. `Dispose()` 是“我现在就主动释放”
|
||||
2. 析构函数是“万一你忘了,我最后帮你兜底一次”
|
||||
3. `GC.SuppressFinalize(this)` 的意思是:
|
||||
这个对象已经手动释放好了,不用再走终结器那一套
|
||||
|
||||
复习重点:
|
||||
- GC 管托管内存
|
||||
- `Dispose` 管需要及时释放的资源
|
||||
- 最常用姿势不是手写 `GC.Collect()`,而是优先写好 `using` / `Dispose`
|
||||
|
||||
#封装
|
||||
#核心
|
||||
|
||||
Reference in New Issue
Block a user