UNITY 单例模式的坑,别写构造函数

本文探讨了在Unity中实现单例模式时遇到的问题,并详细解释了为何不应在构造函数中初始化变量。官方建议使用Awake方法替代,以避免构造函数在编辑模式下被意外调用,从而引发一系列难以预料的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写过C#代码的小伙伴,写单例模式时,习惯如下写法,用静态属性加私有构造函数,OK,一切看上去都不错。

但是,下面的代码,在UNITY中却怎么也不好使,因为继承自MonoBehaviour. 

public class UIManager : MonoBehaviour {

    private static UIManager _instance;
    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            { 
                _instance = new UIManager();
            }
            return _instance;
        }
    }

    private UIManager()
    { 
        print("调用构造函数");
    }
}

如果你把这段代码挂在Gameobject上运行,你会发现构造函数被调用很多次,即使你不挂在GameObject上,在其它脚本中写UIManager.Instance时也会调用构造函数,如下图,你会发现私有的构造函数被多次调用。

一开始我也是百思不得其解,甚至怀疑以前自已写的C#程序写错了,也没仔细去分析,只是写UNITY时所有的单例模式我都不继承自MonoBehaviour.

直到后来,看到了官方解释,终于明白了怎么回事,如下:


Avoid using the constructor. 
避免使用构造函数 
Never initialize any values in the constructor. Instead use Awake or Start 
for this purpose. Unity automatically invokes the constructor even when in edit 
mode. This usually happens directly after compilation of a script, because the 
constructor needs to be invoked in order to retrieve default values of a script. 
Not only will the constructor be called at unforeseen times, it might also be 
called for prefabs or inactive game objects. 
不要在构造函数中初始化任何变量.要用Awake或Start函数来实现.即便是在编辑模式,Unity仍会自动调用构造函数.这通常是在一个脚本编译之后,因为需要调用脚本的构造函数来取回脚本的默认值.我们无法预计何时调用构造函数,它或许会被预置体或未激活的游戏对象所调用. 
In the case of eg. a singleton pattern using the constructor this can have 
severe consequences and lead to seemingly random null reference exceptions. 
单一模式使用构造函数可能会导致严重后果,会带来类似随机的空参数异常. 

So if you want to implement eg. a singleton pattern do not use the the 
constructor, instead use Awake . Actually there is 
no reason why you should ever have any code in a constructor for a class that 
inherits from MonoBehaviour . 
因此,如果你想实现单一模式不要用构造函数,要用Awake函数.事实上,你没必要在继承自MonoBehaviour的类的构造函数中写任何代码.

 

因些,我们在写单例模式时千万不能用构造函数。现在百度搜出来好多代码还有构造函数,简直误导初学者的。

最简单的写法只要用到AWAKE方法就好了,这也是官司方推荐做法

public class UIManager : MonoBehaviour {

    private static UIManager _instance;
    
    void Awake()
    {
        _instance = this;
    }
 
}

 

### 回答1: 在Unity中,实现单例模式的方式与其他编程语言中基本相同。以下是一个简单的例子: ```c# public class Singleton : MonoBehaviour { private static Singleton instance; private void Awake() { if (instance != null && instance != this) { Destroy(gameObject); } else { instance = this; DontDestroyOnLoad(gameObject); } } public static Singleton Instance { get { return instance; } } } ``` 在这个例子中,我们使用了一个静态变量`instance`来存储单例对象。在`Awake`方法中,我们检查是否已经存在单例对象,如果存在则销毁当前对象,否则将当前对象赋值给`instance`并且使用`DontDestroyOnLoad`方法使其在场景切换时不被销毁。同时,我们通过一个静态属性`Instance`来获取单例对象。 在需要使用单例对象的地方,我们可以直接使用`Singleton.Instance`来获取该对象。 ### 回答2: Unity单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点以便在整个项目中使用。 在Unity中,我们经常需要在不同的脚本之间共享数据或者控制某个对象的行为,这时候就可以使用单例模式。使用单例模式可以保证只有一个实例存在,并且可以通过静态方法或属性来访问该实例。 在实现Unity单例模式时,通常会使用一个私有的静态字段来存储该实例,并提供一个公共的静态属性或方法来获取该实例。在获取实例时,首先判断该实例是否已经存在,如果已经存在则直接返回该实例,否则创建一个新实例并返回。这样就可以确保只有一个实例存在,并且可以在需要的地方方便地使用。 例如,我们可以创建一个GameManager类,并使用单例模式来确保只有一个GameManager实例存在。代码示例: ```csharp public class GameManager : MonoBehaviour { private static GameManager instance; private void Awake() { if (instance != null) { Destroy(gameObject); } else { instance = this; DontDestroyOnLoad(gameObject); } } public static GameManager Instance { get { return instance; } } // 其他方法和属性 } ``` 在上述代码中,通过Awake方法来判断实例是否存在,如果存在则销毁当前实例,否则将当前实例赋值给静态的instance字段,并使用DontDestroyOnLoad方法来保证在场景切换时不被销毁。通过Instance属性可以在其他脚本中方便地访问GameManager实例。 使用Unity单例模式可以方便地管理和访问全局的对象和数据,但需要注意的是,过度使用单例模式可能会导致代码的耦合性增加,降低代码的可读性和可维护性,因此在使用时需要权衡利弊。 ### 回答3: Unity中的单例模式是一种常用的设计模式,用于确保某个类在游戏运行过程中只能被实例化一次。 在Unity中,我们通常会把一些需要在多个脚本中共享数据和功能的类设计为单例。这样可以保证在整个游戏中只有一个实例存在,并且其他脚本可以方便地访问这个实例。 实现单例模式需要以下步骤: 1. 首先,我们需要在该类中定义一个私有的静态变量,用来保存类的唯一实例。这个变量的类型应该是类本身。 2. 接下来,我们定义一个公有的静态方法,用来获取该类的实例。在这个方法中,我们需要判断该类的实例是否已经存在,如果不存在就创建一个新的实例,并将其保存到上面定义的变量中。如果已经存在,则直接返回该实例。 3. 最后,我们需要将该类的构造函数标记为私有,以防止在其他地方创建该类的实例。 实现单例模式后,其他脚本只需要通过调用该类的公有静态方法即可获取到类的实例,然后就可以使用这个实例提供的功能和数据了。这样可以避免在游戏中重复创建实例,节省内存开销。 总之,Unity单例模式是一种非常有用的设计模式,能够确保在游戏中某个类只能被实例化一次,并且可以方便地在其他脚本中访问这个实例。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值