Files
My-Notes/笔记/CSharp/核心/构造函数、析构函数、垃圾回收.md
T
2026-06-18 20:01:30 +08:00

6.7 KiB
Raw Blame History

构造函数:

概念: 实例化时,用于初始化的函数 不写默认有一个无参构造函数 ==可以重载==

通常用法: 自定义初始化规则

语法: 访问修饰符 类名(){}

案例:

class Person()
{
	public string name;
	public int age;
	public Person()
	{
		name = "默认值";
		age = 默认值;
	}
}

this关键字: this指当前类声明的成员变量

	class Person()
	{
		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;
		}
	}

特殊写法: 通过this重用构造函数代码,即先调用无参构造函数,然后在调用这个特殊写法的构造函数 语法:访问修饰符 构造函数名(参数列表):this(【可选】参数1,参数2.....) 例子:

		public Person(int age, string name)thisage
		{
			balabala
		}
	过程:先调用无参Person构造函数,然后寻找只有构造单个参数age的构造函数进行初始化

注意: ==函数名和类名必须相同== ==没有特殊用途,一般都是public==

==如果写了有参构造函数且自己没有保留无参构造函数,那么类在实例化时只能使用有参构造函数且无法使用无参构造函数==


析构函数

概念: 当引用类型堆内存被回收时,会调用该函数 对于需要手动管理内存的语言(C++),需要在析构函数中做一些内存回收处理 C#有自动垃圾回收机制GC Unity中基本不用管

通常用法:

语法:

~类名()
{
}

案例:

~Person()
{

}

注意:


垃圾回收

垃圾回收(GC) 通过遍历堆Heap上动态分配的所有对象 通过识别他们是否被引用来确定哪些对象时垃圾,哪些对象仍然需要引用 垃圾:没有变量或对象引用的内容 如果是垃圾就要被回收释放

一般都是==被动触发(会造成卡顿)==,所以游戏一般需要手动执行垃圾回收 语法:GC.Collect(); ==GC会造成卡顿,所以在加载Loading时一般会调用==

垃圾回收有多种算法 引用计数 标记清楚 标记整理 复制集合

注意: GC只负责Heap内存的垃圾祸首 引用类型都是在Heap中的,所以他需要GC来进行管理

栈Stack上的内存时系统自动管理的 所以不需要GC

C#中垃圾回收机制的大概原理 他会把内存分为0,1,2代内存 代的概念是垃圾回收中的一种算法需要 新分配的对象都会被配置在第0代内存中 每次分配都可能会进行垃圾回收(一般来说0代内存满了就会触发) 在第一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行一下两部 1.标记对象,从根(静态参数、方法参数)开始检查引用对象,标记后为可达对象,未标记就是不可大对象 不可达对象就会认为是垃圾 2.搬迁对象 压缩堆 (挂起执行内存托管代码线程:把第0代内存(还不是垃圾的内存)放到一代进行搬迁)释放未标记的对象、搬迁可达对象、修改引用地址

大对象总被认为是二带内存,目的是减少性能损耗 不会对大对象进行压缩搬迁 大对象是85000kb以上的

示意图 step1:程序放入内存 !Pasted image 20260412210327.png 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 专门定义的释放资源标准接口

标准释放模式

例子:

using System;

// 1. 实现 IDisposable 接口
public class MyResource : IDisposable
{
    // 标记资源是否已释放
    private bool _disposed = false;

    // 2. 公共释放方法(外部调用)
    public void Dispose()
    {
        Dispose(true);
        // 告诉GC:不用再管我了,我已经手动释放完了
        GC.SuppressFinalize(this);
    }

    // 3. 核心释放逻辑
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 释放 托管资源
        }

        // 释放 非托管资源(比如关闭文件、关闭数据库连接)
        // 这里写你的资源释放代码

        _disposed = true;
    }

    // 4. 析构函数:防止用户忘记调用 Dispose
    ~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清理,不会造成太大的性能问题==

如果需要==一次性==彻底释放 例子:

var resource1 = new MyResource();
resource1.Dispose();

//手动切断引用
resource1 = null;
//手动触发GC
GC.Collection();
GC.WaitForPendingFinalizers();//等待回收完成

#封装 #核心