Singleton

思考并回答以下问题:

  • 为什么管理类都使用单例模式?

本章涵盖:

Singleton 的调用方式:

1
xxx.Instance

如何实现一个单例?

1. C# 类 通过继承 QSingleton

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
namespace QFramework.Example
{
using UnityEngine;

class Class2Singleton :QSingleton<Class2Singleton>
{
private static int mIndex = 0;

private Class2Singleton() {}

public override void OnSingletonInit()
{
mIndex++;
}

public void Log(string content)
{
Debug.Log("Class2Singleton" + mIndex + ":" + content);
}
}

public class Singleton : MonoBehaviour
{
private void Start()
{
Class2Singleton.Instance.Log("Hello World!");

// delete instance
Class2Singleton.Instance.Dispose();

// a differente instance
Class2Singleton.Instance.Log("Hello World!");
}
}
}

2. MonoBehaviour 类 通过继承 QMonoSingleton

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
namespace QFramework.Example
{
using System.Collections;
using UnityEngine;

class Class2MonoSingleton : QMonoSingleton<Class2MonoSingleton>
{
public override void OnSingletonInit()
{
Debug.Log(this.name + ":" + "OnSingletonInit");
}

private void Awake()
{
Debug.Log(this.name + ":" + "Awake");
}

private void Start()
{
Debug.Log(this.name + ":" + "Start");
}

protected override void OnDestroy()
{
base.OnDestroy();

Debug.Log(this.name + ":" + "OnDestroy");
}
}
}

3. C# 类 通过实现静态 Instance 属性器

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
namespace QFramework.Example
{
using UnityEngine;

class Class2SignetonProperty : ISingleton
{
public static Class2SignetonProperty Instance
{
get { return QSingletonProperty<Class2SignetonProperty>.Instance; }
}

private Class2SignetonProperty() {}

private static int mIndex = 0;

public void OnSingletonInit()
{
mIndex++;
}

public void Dispose()
{
QSingletonProperty<Class2SignetonProperty>.Dispose();
}

public void Log(string content)
{
Debug.Log("Class2SingletonProperty" + mIndex + ":" + content);
}
}
}

4. MonoBehaivour 类 通过实现静态 Instance 属性器

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
namespace QFramework.Example
{
using System.Collections;
using UnityEngine;

class Class2MonoSingletonProperty : MonoBehaviour,ISingleton
{
public static Class2MonoSingletonProperty Instance
{
get { return QMonoSingletonProperty<Class2MonoSingletonProperty>.Instance; }
}

public void Dispose()
{
QMonoSingletonProperty<Class2MonoSingletonProperty>.Dispose();
}

public void OnSingletonInit()
{
Debug.Log(name + ":" + "OnSingletonInit");
}

private void Awake()
{
Debug.Log(name + ":" + "Awake");
}

private void Start()
{
Debug.Log(name + ":" + "Start");
}

protected void OnDestroy()
{
Debug.Log(name + ":" + "OnDestroy");
}
}
}

5. 对 GameObject 进行命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace QFramework.Example
{
using UnityEngine;

[QMonoSingletonPath("[Example]/QMonoSingeltonPath")]
class ClassUseMonoSingletonPath : QMonoSingleton<ClassUseMonoSingletonPath>
{

}

public class MonoSingletonPath : MonoBehaviour
{
private void Start()
{
var intance = ClassUseMonoSingletonPath.Instance;
}
}
}

代码

ISingleton.cs

1
2
3
4
5
6
7
namespace QF
{
public interface ISingleton
{
void OnSingletonInit();
}
}

MonoSingleton.cs

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
50
namespace QF
{
using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour, ISingleton where T : MonoSingleton<T>
{
protected static T mInstance = null;

public static T Instance
{
get
{
if (mInstance == null)
{
mInstance = MonoSingletonCreator.CreateMonoSingleton<T>();
}

return mInstance;
}
}

public virtual void OnSingletonInit()
{
}
public virtual void Dispose()
{
if (MonoSingletonCreator.IsUnitTestMode)
{
var curTrans = transform;
do
{
var parent = curTrans.parent;
DestroyImmediate(curTrans.gameObject);
curTrans = parent;
} while (curTrans != null);

mInstance = null;
}
else
{
Destroy(gameObject);
}
}

protected virtual void OnDestroy()
{
mInstance = null;
}
}
}

MonoSingletonCreator.cs

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
namespace QF
{
using System.Reflection;
using UnityEngine;

public static class MonoSingletonCreator
{
public static bool IsUnitTestMode { get; set; }

public static T CreateMonoSingleton<T>() where T : MonoBehaviour, ISingleton
{
T instance = null;

if (!IsUnitTestMode && !Application.isPlaying) return instance;
instance = Object.FindObjectOfType<T>();

if (instance != null)
{
instance.OnSingletonInit();
return instance;
}

MemberInfo info = typeof(T);
var attributes = info.GetCustomAttributes(true);
foreach (var atribute in attributes)
{
var defineAttri = atribute as MonoSingletonPath;
if (defineAttri == null)
{
continue;
}

instance = CreateComponentOnGameObject<T>(defineAttri.PathInHierarchy, true);
break;
}

if (instance == null)
{
var obj = new GameObject(typeof(T).Name);
if (!IsUnitTestMode)
Object.DontDestroyOnLoad(obj);
instance = obj.AddComponent<T>();
}

instance.OnSingletonInit();
return instance;
}

private static T CreateComponentOnGameObject<T>(string path, bool dontDestroy) where T : MonoBehaviour
{
var obj = FindGameObject(path, true, dontDestroy);
if (obj == null)
{
obj = new GameObject("Singleton of " + typeof(T).Name);
if (dontDestroy && !IsUnitTestMode)
{
Object.DontDestroyOnLoad(obj);
}
}

return obj.AddComponent<T>();
}

private static GameObject FindGameObject(string path, bool build, bool dontDestroy)
{
if (string.IsNullOrEmpty(path))
{
return null;
}

var subPath = path.Split('/');
if (subPath == null || subPath.Length == 0)
{
return null;
}

return FindGameObject(null, subPath, 0, build, dontDestroy);
}

private static GameObject FindGameObject(GameObject root, string[] subPath, int index, bool build, bool dontDestroy)
{
GameObject client = null;

if (root == null)
{
client = GameObject.Find(subPath[index]);
}
else
{
var child = root.transform.Find(subPath[index]);
if (child != null)
{
client = child.gameObject;
}
}

if (client == null)
{
if (build)
{
client = new GameObject(subPath[index]);
if (root != null)
{
client.transform.SetParent(root.transform);
}

if (dontDestroy && index == 0 && !IsUnitTestMode)
{
GameObject.DontDestroyOnLoad(client);
}
}
}

if (client == null)
{
return null;
}

return ++index == subPath.Length ? client : FindGameObject(client, subPath, index, build, dontDestroy);
}
}
}

MonoSingletonPath.cs

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
namespace QF
{
using System;

public class QMonoSingletonPath : MonoSingletonPath
{
public QMonoSingletonPath(string pathInHierarchy) : base(pathInHierarchy)
{
}
}

[AttributeUsage(AttributeTargets.Class)]
public class MonoSingletonPath : Attribute
{
private string mPathInHierarchy;

public MonoSingletonPath(string pathInHierarchy)
{
mPathInHierarchy = pathInHierarchy;
}

public string PathInHierarchy
{
get { return mPathInHierarchy; }
}
}
}

MonoSingletonProperty.cs

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
namespace QF
{
using UnityEngine;

public static class MonoSingletonProperty<T> where T : MonoBehaviour, ISingleton
{
private static T mInstance = null;

public static T Instance
{
get
{
if (null == mInstance)
{
mInstance = MonoSingletonCreator.CreateMonoSingleton<T>();
}

return mInstance;
}
}

public static void Dispose()
{
if (MonoSingletonCreator.IsUnitTestMode)
{
Object.DestroyImmediate(mInstance.gameObject);
}
else
{
Object.Destroy(mInstance.gameObject);
}

mInstance = null;
}
}

[System.Obsolete("弃用啦,请使用 MonoSingletonProperty")]
public static class QMonoSingletonProperty<T> where T : MonoBehaviour, ISingleton
{
public static T Instance
{
get { return MonoSingletonProperty<T>.Instance; }
}
}
}

Singleton.cs

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
namespace QF
{
public abstract class Singleton<T> : ISingleton where T : Singleton<T>
{
protected static T mInstance;

static object mLock = new object();

protected Singleton()
{
}

public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
mInstance = SingletonCreator.CreateSingleton<T>();
}
}

return mInstance;
}
}

public virtual void Dispose()
{
mInstance = null;
}

public virtual void OnSingletonInit()
{
}
}

[System.Obsolete("弃用啦,建议用 Singleton")]
public abstract class QSingleton<T> : Singleton<T> where T : QSingleton<T>
{
protected QSingleton()
{
}
}
}

SingletonCreator.cs

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
namespace QF
{
using System;
using System.Reflection;

public static class SingletonCreator
{
public static T CreateSingleton<T>() where T : class, ISingleton
{
// 获取私有构造函数
var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

// 获取无参构造函数
var ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);

if (ctor == null)
{
throw new Exception("Non-Public Constructor() not found! in " + typeof(T));
}

// 通过构造函数,常见实例
var retInstance = ctor.Invoke(null) as T;
retInstance.OnSingletonInit();

return retInstance;
}
}
}

SingletonProperty.cs

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
namespace QF
{
public static class SingletonProperty<T> where T : class, ISingleton
{
private static T mInstance;
private static readonly object mLock = new object();

public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
mInstance = SingletonCreator.CreateSingleton<T>();
}
}

return mInstance;
}
}

public static void Dispose()
{
mInstance = null;
}
}

[System.Obsolete("弃用啦,请使用 SingletonProperty")]
public static class QSingletonProperty<T> where T : class, ISingleton
{
public static T Instance
{
get { return SingletonProperty<T>.Instance; }
}
}
}
0%