单例模式实在太常用了,有时各种 manger 各种 tool 都需要使用单例模式。
但是在 像 unity 这种组件模式的代码结构中,去写单例模式,有时还是会很棘手~~
那么先开始的开始开始吧~~~
我愿我是一个愉快的搬运工~
c# 单例模板
当你脑海中想获取一个单例的时候,你手毫不犹豫的就写成这样了,
嗯,我在 winForm 程序都是这样写的,挺好的并没有发生什么问题,连你觉得这样都是对的时候,可能就在为自己的懒惰找借口了~ 好,你问我要不要改,我才不会改了~。。。
public class XXXManager() {
public static XXXManager instance = new XXXManager();
}
}
这种有什么缺点呢,
1. 程序在一开始的时候就会创建实例对象,浪费内存
2. 这种并不能算做单例模式,因为它任然可以再次实例化
于是我们改进下它,
public class XXXManager() {
public static XXXManager mInstance = null;
public static Instance
{
get
{
if(mInstance == null) mInstance = new XXXManager();
return mInstance;
}
}
}
}
现在我们更进了一步,起码内存是在我需要这个实例的时候再去创建它,避免内存浪费。
但是我们觉得这还是不够的,并且我们觉得还是太麻烦了,假如这样的单例太多了,那么每一个都这样写,那么这样并不太好。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
public abstract class Singleton<T> where T : Singleton<T>
{
protected static T instance = null;
protected Singleton()
{
}
public static T Instance()
{
if (instance == null)
{
// 先获取所有非public的构造方法
ConstructorInfo[] ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 从ctors中获取无参的构造方法
ConstructorInfo ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
if (ctor == null)
throw new Exception("Non-public ctor() not found!");
// 调用构造方法
instance = ctor.Invoke(null) as T;
}
return instance;
}
}
我们这样有一个 抽象模板单例类 Singleton ,用泛型去区分类型,注意这里我们的构造函数是 protected 类型的,也就是说我不能在其它地方去创建它的实例,因为它实在没有构造函数了。
但是我们要创建实例怎么办? 好在 c# 有反射,去创建实例。这样我们一个比较好的单例模式就基本成型了。
MonoBehaviour 单例模板
搬运中~~~~~~
如何设计接收MonoBehaviour生命周期的单例的模板?
如何设计?
先分析下需求:
1.约束脚本实例对象的个数。
2.约束GameObject的个数。
3.接收MonoBehaviour生命周期。
4.销毁单例和对应的GameObject。
using UnityEngine;
/// <summary>
/// 需要使用Unity生命周期的单例模式
/// </summary>
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
protected static T instance = null;
public static T Instance()
{
if (instance == null)
{
instance = FindObjectOfType<T>();
if (FindObjectsOfType<T>().Length > 1)
{
return instance;
}
if (instance == null)
{
string instanceName = typeof(T).Name;
GameObject instanceGO = GameObject.Find(instanceName);
if (instanceGO == null)
instanceGO = new GameObject(instanceName);
instance = instanceGO.AddComponent<T>();
DontDestroyOnLoad(instanceGO); //保证实例不会被释放
}
else
{
}
}
return instance;
}
protected virtual void OnDestroy()
{
instance = null;
}
}
这里会首先判断挂载实例的数目, 永远只需要一个实例,然后 DontDestroyOnLoad, 确保它不会被销毁。
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;
}
}
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);
}
}
在这里 UniWb 里 封装了 MonoSingletonCreator.CreateMonoSingleton 这个方法,让创建这样的实例会很方便,但原理都是相同的
这样一来我们单例才算真正的能很好的使用了~~~~