常见的单例模式为:
不继承MONO:
public class Singleton{
private static Singleton m_instance;
public static Singleton Instance
{
get
{
if (m_instance == null)
{
m_instance = new Singleton();
}
return m_instance;
}
}
private Singleton()
{
}
}
这里因为在unity中,对于线程安全那些就不考虑了。必要时加个锁(lock)。注意的两点是
1.防止外部再实例化这个类,要把构造函数设置为私有。
2.不继承MONO,可以直接通过new创建单例,如果继承MONO,上述方法无效,原因在于MONO是依附于GameObject的,不能通过new直接实例化继承MONO的类。
继承MONO:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class MonoSingleton :MonoBehaviour where T: MonoBehaviour
{
private static T m_instance;
public static T Instance
{
get
{
if (m_instance == null)
{
m_instance = Object.FindObjectOfType();
if (m_instance == null)
{
GameObject obj=new GameObject("MonoSingleton");
m_instance = obj.AddComponent();
Object.DontDestroyOnLoad(obj);
}
}
return m_instance;
}
}
}
上面直接写了一个继承MONO的泛型类,任何继承MONO的单例直接引用这个就可以。下面重点介绍不继承MONO的单例写法,与最上面基本写法区别在于,不需要在每个单例中都写那么一大段单例,只需要继承该类即可。
泛型类:Singleton
public abstract class Singleton where T : class, new()
{
protected static T _instance = null;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = new T();
}
return _instance;
}
}
protected Singleton()
{
Init();
}
public virtual void Init()
{
}
}
这种通过泛型的方式好处在于继承它的类可以全部以单例去实现,不需要单独再写。但是!!要注意两点:
1.必须加上new(),这样才能对它进行实例化,不然会报下列错误
2.虽然这样可以省掉每个类中单独去申明变量,但也导致将类的构造函数暴露了出来。
比如这里你新建了一个类继承它,那构造函数就无法设置成private,这样就导致单例的不合理性。
至此,上面的问题暴露出来的问题是
如果每个单例类单独写Instance变量,虽然可以实现构造方法私有,但比较繁琐。
如果统一通过泛型类去做单例,虽然书写简单了很多,但是切导致构造函数暴露。
这也印出来本文的重头戏:用反射,解决以上两个问题,实现单例的效果
通过反射实现单例:
public abstract class Singleton where T : class
{
private static T m_intance;
public static T Instance
{
get
{
if (null == m_intance)
{
m_intance = null;
Type type = typeof (T);
System.Reflection.ConstructorInfo[] constructorInfoArray =
type.GetConstructors(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
foreach (System.Reflection.ConstructorInfo constructorInfo in constructorInfoArray)
{
System.Reflection.ParameterInfo[] parameterInfoArray = constructorInfo.GetParameters();
if (0 == parameterInfoArray.Length)
{
m_intance = (T) constructorInfo.Invoke(null);
break;
}
}
if (null == m_intance)
{
throw new NotSupportedException("No NonPublic constructor without 0 parameter");
}
}
return m_intance;
}
}
protected Singleton()
{
}
public static void Destroy()
{
m_intance = null;
}
}
简单解释一下反射的原理,首先将类型强转,Type type = typeof (T);然后获取到它的构造函数的类型和参数信息。这里做了一个监测,如果构造函数是私有或者静态,并且构造函数无参,才会进行单例的实现。
所以这里用反射的原因就在于,可以通过反射去调用私有的构造函数,实现了构造函数不对外暴露的同时,实现了单例的简化,不需要每个单例类单独书写。
唯一的缺点,应该就是这种方式比较复杂,稍微多了一点空间上的内存和一丁点性能,可以忽略不计。这里引用一个别人对于反射的博客:
https://www.jianshu.com/p/2f0cfdf116c8
至此,单例的几种模式就介绍完了,个人喜欢直接把反射方式做成一个公共库中的类,每个项目直接调用就OK。有什么不懂和意见,欢迎留言咨询。