关于Unity中的C#构造函数与this关键词的使用。

这个应该属于撸一个适合自己的小游戏框架的第四篇。上一篇是Unity网格合并,Matrix4x4,和InverseTransformPoint的简单理解。
这里在写小框架的对象池的时候。参考其他人代码的时候看到了这样的代码

public class SpawnPool
	{
        private string _goName = string.Empty;
        //Pool中当前的对象数量
		private int _nowCount;
        //Pool中对象的最大数量
        private int _maxCount;
        private GameObject _prefab;
		private Transform _parent = null;
        private bool _isAutoHide = false;
        private float _autoHideTimer = -1;
        //显示对应的list
		private List<GameObject> _trueListGo;
        //隐藏物体的list
		private List<GameObject> _falseListGo;
        public int TrueCount { get { return this._trueListGo.Count; } }
        public int FalseCount { get { return this._falseListGo.Count; } }


        public SpawnPool(string _GoName,int _MaxCount, GameObject _Prefab, Transform _Tra):this(_GoName,_MaxCount,_Prefab,_Tra,false,-1)
        {

            
        }

        public SpawnPool (string _GoName,int _MaxCount, GameObject _Prefab, Transform _Tra,bool _IsAutoHide,float _AutoHideTimer)
		{
            this._goName = _GoName;
			this._parent = _Tra;
			this._maxCount = _MaxCount;
			this._prefab = _Prefab;
            this._isAutoHide = _IsAutoHide;
            this._autoHideTimer =_AutoHideTimer;

			this._trueListGo = new List<GameObject>();
			this._falseListGo = new List<GameObject>();
			this._dicGoSurviveTimer = new Dictionary<GameObject,int>();
            //每次克隆物体会+1
            this._nowCount = 0;
            //生成的pool时初始化一个隐藏物体。
            GameObject go = this.InsGo ();
			go.transform.SetParent(_Tra);
			go.transform.localPosition = Vector3.zero;		
			this.HideGo(go);
		}
}

很简单的构造函数,只是用了this关键字。通过运行发现传两种参数来调用方法都走下面6个参数的构造。
然后找到了this这种用法。用this串联构造函数。

结论如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class test : MonoBehaviour {

    void Start()
    {
        //实例化test
        test test = new test("111");

        test test1 = new test("222",1);

    }
    /// <summary>
    /// 无参构造函数函数。继承MonoBehaviour。
    /// 状况1:当挂载到场景中的就会执行一次。
    /// 状况2:开始运行的时候会自动执行两次。
    /// 状况3:关闭运行的时候会自动执行一次。
    /// 结论:MonoBehaviour有两个生命周期,一个是作为C#对象的周期,
    /// 一个是作为Component的周期。构造函数代表第一个,Awake代表第二个。
    /// 即你的代码既可以作为Unity的脚本,也可以作为C#的脚本执行。
    /// 在不同的生命周期执行会表现的跟Player环境下不一样。
    /// 所以尽量不要用构造函数(很多文档都提到过这个),设计上需要用也要保证初始化代码尽可能简单。
    /// </summary>
    public test()
    {
        Debug.LogError("无参构造函数");
    }

    /// <summary>
    /// 有参构造函数。使用this关键字
    /// 状况:会先执行this后面串联的构造函数然后再执行自己的方法。
    /// </summary>
    /// <param name="text"></param>
    public test(string text) : this(text, 0)
    {
        Debug.LogError("1个参数的构造函数:");
        Debug.LogError("text:" + text);
    }
    /// <summary>
    /// 有参构造函数。不使用this关键字
    /// 状况:必须用包含对应参数的实例化方法才能执行。
    /// </summary>
    /// <param name="text"></param>
    /// <param name="x"></param>
    public test(string text,int x)
    {
        Debug.LogError("2个参数的构造函数:");
        Debug.LogError("text:" + text);
        Debug.LogError("x:"+x);
    }
}

执行结果以上。

<think>嗯,用户问的是C#中的析构函数原理。之前他问过Unity的UI系统,现在转向C#语言特性,可能是在学习Unity开发中遇到了内存管理的问题。需要先确定用户的基础,可能对垃圾回收机制不太清楚,或者对析构函数和Finalize方法的关系有疑问。 首先,析构函数在C#中是用~ClassName()的语法定义的,但实际它会被编译器转换成Finalize方法。需要解释清楚这一点,避免混淆。用户可能以为析构函数像C++那样直接调用,但在C#中是由GC管理的。 接下来要说明析构函数的工作原理,包括GC何时调用,以及Finalize方法在垃圾回收过程中的角色。需要提到析构函数的不确定性,即无法预测何时执行,这点和Dispose方法不同,后者是确定性的。 然后,用户可能想知道析构函数的使用场景。比如处理非托管资源,但更佳实践是使用IDisposable接口。这时候需要对比析构函数和Dispose的区别,强调析构函数作为备份机制,防止资源泄漏。 还要注意性能问题,因为Finalize方法会导致对象晋升到下一代,增加GC负担。因此建议避免不必要的析构函数,特别是托管资源不需要它们。 用户可能混淆了析构函数和Dispose方法,所以需要明确两者的使用场景。比如,非托管资源应该用Dispose,析构函数作为最后的保障。同时,给出代码示例,说明如何正确实现Dispose模式,结合using语句。 最后,总结析构函数的适用场景和注意事项,确保用户理解何时使用以及潜在的性能影响。可能需要提醒用户,在Unity开发中,资源管理尤为重要,正确释放资源可以避免内存泄漏和性能问题。 确保回答结构清晰,分点说明,避免技术术语过于复杂,用简单的中文解释概念。同时,检查是否有遗漏点,比如析构函数的执行顺序、多个析构函数的情况,但可能用户更关注基础原理,所以保持回答简明。</think>在 C# 中,**析构函数(Destructor)** 是一种特殊的成员函数,其核心原理 **垃圾回收(Garbage Collection, GC)** 机制紧密相关。以下是逐层解析: --- ### **1. 基本定义语法** - **语法形式**: ```csharp class MyClass { ~MyClass() { // 清理非托管资源的代码(如文件句柄、网络连接等) } } ``` - **注意**:析构函数名称固定为 `~类名`,无参数、无返回值、不可重载。 - **底层原理**: - 编译器会将析构函数编译为 `Finalize()` 方法,等价于: ```csharp protected override void Finalize() { try { // 析构函数代码 } finally { base.Finalize(); // 调用基类的 Finalize 方法(如果存在) } } ``` --- ### **2. 执行机制** #### **关键特性** | 特性 | 说明 | |---------------------|----------------------------------------------------------------------| | **非确定性销毁** | 析构函数由垃圾回收器(GC)触发,无法预测何时执行 | | **执行顺序** | 从派生类到基类逐级调用(构造函数顺序相反) | | **线程安全性** | GC 线程调用析构函数,需注意多线程资源访问问题 | | **性能成本** | 含析构函数的对象需要两次 GC 才能完全回收(晋升到下一代) | #### **生命周期流程** 1. **对象创建**:通过 `new` 实例化对象。 2. **对象使用**:对象存活于托管堆中。 3. **对象不可达**:当对象不再被任何根(Root)引用时,GC 将其标记为“可回收”。 4. **析构调用**: - 若对象定义了析构函数,GC 将其放入 **终结队列(Finalization Queue)**。 - 专用 **终结线程(Finalizer Thread)** 异步执行 `Finalize()` 方法。 5. **最终回收**:执行完 `Finalize()` 的对象被移入 **可终结对象队列**,下次 GC 时彻底释放内存。 --- ### **3. 使用场景限制** #### **适用场景** - **清理非托管资源**:如文件句柄 (`FileStream`)、数据库连接、网络套接字等。 - **应急兜底**:当未正确实现 `IDisposable` 模式时,析构函数作为最后的资源释放保障。 #### **不适用场景** - **托管资源**:如其他 .NET 对象(GC 会自动回收,无需手动清理)。 - **高频调用**:析构函数执行延迟且不可控,不适合实时性要求高的场景。 --- ### **4. `IDisposable` 模式的对比** | **对比维度** | 析构函数 (`Finalize()`) | `IDisposable` 接口 | |----------------------|------------------------------------------|-------------------------------------------| | **控制权** | 由 GC 控制 | 由开发者显式调用(如 `using` 语句) | | **执行时机** | 不确定(依赖 GC 触发) | 确定(调用 `Dispose()` 时立即执行) | | **性能影响** | 高(对象需两次 GC 回收) | 低(直接释放资源) | | **代码复杂度** | 简单(自动触发) | 需手动管理,结合 `using` 或 `try-finally` | #### **最佳实践:结合两者** ```csharp class ResourceHolder : IDisposable { private bool _disposed = false; // 实现 IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // 避免重复调用 Finalize } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // 释放托管资源 } // 释放非托管资源(无论 disposing 是 true 或 false) _disposed = true; } } // 析构函数(兜底清理) ~ResourceHolder() { Dispose(false); } } ``` --- ### **5. 注意事项** 1. **避免空析构函数**:无意义的析构函数会导致不必要的性能损失。 2. **不依赖执行顺序**:无法保证多个对象的析构函数调用顺序。 3. **异常处理**:析构函数中的未处理异常会导致进程崩溃(.NET Framework 中)或静默失败(.NET Core+ 中)。 4. **替代方案**:优先使用 `SafeHandle` 派生类(如 `FileStream` 内部实现)管理非托管资源。 --- ### **总结** - **析构函数本质**:是 `Finalize()` 方法的语法糖,由 GC 异步调用。 - **核心用途**:仅作为非托管资源释放的“最后防线”,需 `IDisposable` 配合使用。 - **性能代价**:含析构函数的对象生命周期更长,应谨慎使用。 实际开发中,**99% 的场景应优先使用 `IDisposable` 模式**,析构函数仅作为防止资源泄漏的保险机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值