Unity之单例模式基类

最近在学习Unity程序框架,于是我来记录一下单例模式基类


单例模式基类是什么?就是通过写一个基类脚本、再用其他脚本继承基类脚本实现功能的一种方式,下面是一个简单的基类脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BassManager<T> where T:class,new()
{
private static T instance;
//注意这里我们没有私有化构造函数,其实是不必要的,你知道我们在用单例模式就不会new一个实例了。
public static T Instance
{
get
{
if(instance == null)
instance = new T();
return instance;
}
}
}

接下来我们写的脚本,只需要继承这个单例模式基类,比如:

1
2
3
4
5
6
7
8
9
10
11
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test: BassManager<Test>
{
public void Speak()
{
Debug.Log("我调用了方法");
}
}

之后我们在使用Test脚本想调用方法、又不想创建新实例(用单例模式)时,就只需要Test.Instance().Speak()
潜在的问题,是多线程


此外还写了两种单例模式基类,一类是继承MonoBehaviour的,一类是没继承的。
这里我稍微解释一下关于“lock”和“自动式挂载”的问题。首先是lock锁,lock锁的存在是为了解决多线程访问的问题。

我的建议是,继承Mono的脚本就不用加lock了,因为Unity给我们制定好了规则,不会出现多线程访问的问题;不继承Mono的脚本就加上lock。

关于“自动式挂载”,你可能见到,有的继承了Mono的代码写法和我不一样,它们会多出一个Awake方法。我感觉我的代码更好一些,因为会自动挂载脚本,大家辩证看待、互相学习就好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingleMono<T> : MonoBehaviour where T : MonoBehaviour
{
//这个是继承MonoBehaviour的(我发现这个不是最安全的,可能会线程泄露,请继续阅读我25.1.28补充的单例模式)
private static T instance;
public static T Instance()
{
if(instance == null)
{
GameObject go = new GameObject(typeof(T).ToString());
//过场景不移除
DontDestroyOnLoad(go);
instance = go.AddComponent<T>();
}
return instance;
}
}

没继承的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SingletonBase<T> where T : class, new()
{
private static T instance;
protected static readonly object _lock = new object();
public static T GetInstance()
{
if (instance == null)
{
lock(_lock)
{
if (instance == null)
{
instance = new T();
}
}
}
return instance;

}
}


2025.1.28更新
我发现继承Mono的单例模式基类不安全、有隐患,于是又写了一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using UnityEngine;

public class MonoSingleTwo<T> : MonoBehaviour where T : MonoSingleTwo<T>
{
private static T _instance;
private static object _lock = new object();

public static T MainInstance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance = FindObjectOfType<T>() as T; //先去场景中找有没有这个类

if (_instance == null)//如果没有,那么我们自己创建一个Gameobject然后给他加一个T这个类型的脚本,并赋值给instance;
{
GameObject go = new GameObject(typeof(T).Name);
_instance = go.AddComponent<T>();
}
}
}

return _instance;
}
}


protected virtual void Awake()
{
if (_instance == null)
{
_instance = (T)this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}


private void OnApplicationQuit()//程序退出时,将instance清空
{
_instance = null;
}
}