保证一个类只有一个实例,并且提供了访问该实例的全局访问点。
尽管它的意图是好的,GoF描述的单例模式通常弊大于利。 他们强调应该谨慎使用这个模式,但在游戏业界的口口相传中,这一提示经常被无视了。
由于单例只有被使用的时候才会创建实例,不使用则不会被创建。所以节约内存和CPU循环总是好的,但是就像其他模式一样,在不合适的地方使用单例模式就好像用夹板处理子弹伤口。 由于它被滥用得太严重了,所以请适当在合适的地方使用。(QAQ记得开始出来工作疯狂单例狂)
先来看一个正常单例
public class TestSingle
{
protected static TestSingle instance;
public static TestSingle Instance
{
get
{
if (instance == null)
instance = new TestSingle();
return instance;
}
}
public void Debug()
{
Console.WriteLine("TestSingleton");
}
public void Dispose()
{
instance = null;
}
}
一个静态的全局变量,当为空的时候创建一个,保证全局就一个
但是当整个工程里有大量单例类的时候,需要每个都写这样重复代码就很操蛋了。当然你说你公司是按照代码量来绩效考核那准没问题了- -,所以此时需要引入泛型单例。
public class SingletonComponent<T>where T : new()
{
protected static T instance;
public static T Instance
{
get
{
if (instance == null)
instance = new T();
return instance;
}
}
}
此时泛型单例已经解决了重复劳动的活了,模板化。
但是此时,如果一个类是需要继承单例并且还要继承其他类时候,在多继承的时候,此时泛型单例并不满足(至少在C#是不允许的)。比如这种情况:
需求继承BaseTest时候在继承Singleton,C#是不允许这么做的,写了编译的时候直接会报错
public class TTestSingleton:BaseTest
{
private TTestSingleton()
{
Console.WriteLine("TTestSingleton Is New");
}
public void Debug()
{
Console.WriteLine("TTestSingleton");
}
}
此时解决办法是:如果让单例充当属性,就能很完美解决这个问题
public class SingletonComponent<T>where T:class
{
protected static T instance;
protected SingletonComponent()
{
}
public static T Instance
{
get
{
if (instance == null)
{
//获取私有构造函数
ConstructorInfo[] ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
//无参数构造函数
ConstructorInfo ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
if (ctor == null)
throw new Exception("Not Find Non-Public ctors()");
instance = ctor.Invoke(null) as T;
}
return instance;
}
}
public void Dispose()
{
instance = null;
}
}
public class TTestSingleton:BaseTest
{
private TTestSingleton()
{
Console.WriteLine("TTestSingleton Is New");
}
public void Debug()
{
Console.WriteLine("TTestSingleton");
}
public static TTestSingleton Instance
{
get
{
return SingletonComponent<TTestSingleton>.Instance;
}
}
}
public class BaseTest
{
public virtual void TEST()
{
Console.WriteLine("TEST");
}
}
TTsingleton就能在继承其他Base类的时候也能充当单例模块。
为了可以被继承,静态实例和构造方法都使用protect修饰符
此时问题是不能New一个泛型,(并不是不能New一个泛型出来)如果New的话不能显示调用私有构造函数,但是泛型如果使用New()进行约束,就能New了,
但是其实最终也是进行了t = System.Activator.CreateInstance<T>();这操作
所以直接使用反射来进行构造。
来让看下在UnityMono中单例是如何实现:
Mono中使用单例 ,也是保证全局唯一,并且有检查唯一性方法
这里单例模板也是使用了反射,利用特性进行打上标签,进行生产的时候可以根据标签来生成
ISingleton接口可以让单例模板进行更多操作,当前写了一个OnInitSingle初始化办法
public abstract class MonoSingletonComponent<T>:MonoBehaviour,ISingleton where T:MonoSingletonComponent<T>
{
protected static T instance;
public static T Instance
{
get
{
instance = FindObjectOfType<T>();
if (FindObjectsOfType<T>().Length > 1)
throw new System.Exception("Mono Type" + typeof(T).Name + "More Than One");
if (instance == null)
{
string typeName = string.Empty;
SingletonPathAttribute attribute = typeof(T).GetCustomAttribute<SingletonPathAttribute>();
if (attribute == null)
typeName = typeof(T).Name;
else
typeName = attribute.MPathInHierarchyName;
GameObject[] objs = FindSingletonPathAttributeGameObject.Find(typeName);
//最后一个节点进行添加脚本
instance = objs[0].AddComponent<T>();
//DontDestroyOnLoad 如果是子节点会给警告,需要进行父节点DontDestroyOnLoad
DontDestroyOnLoad(objs[1].gameObject);
}
else
{
Debug.Log(string.Format("{0} is already exit", typeof(T).Name));
}
return instance;
}
}
public virtual void OnDestroy()
{
instance = null;
}
public virtual void OnInitSingleton()
{
}
public virtual void Dispose()
{
UnityEngine.Object.DestroyImmediate(instance.gameObject);
}
}
public interface ISingleton
{
void OnInitSingleton();
}
public class SingletonPathAttribute : System.Attribute
{
private string mPathInHierarchyName;
public SingletonPathAttribute(string _pathName)
{
mPathInHierarchyName = _pathName;
}
public string MPathInHierarchyName { get => mPathInHierarchyName;}
}
public class FindSingletonPathAttributeGameObject
{
public static GameObject[] Find(string path)
{
GameObject[] objs = new GameObject[2];
string[]pathArray=path.Split('/');
if (pathArray.Length <= 0)
{
objs[0] = GameObject.Find(path);
objs[1] = objs[0];
}
else
{
for (int i = 0; i < pathArray.Length; i++)
{
if (objs[0] == null)
objs[0] = GameObject.Find(pathArray[i]);
else
objs[0] = objs[0].transform.Find(pathArray[i]).gameObject;
if (i == 0)
objs[1] = objs[0];
}
}
if (objs[0] == null)
{
if (pathArray.Length <= 0)
{
objs[0] = new GameObject(path);
objs[1]= objs[0];
}
else
{
for (int i = 0; i < pathArray.Length; i++)
{
if (objs[0] == null)
{
objs[0] = new GameObject(pathArray[i]);
}
else
{
GameObject _newObj= new GameObject(pathArray[i]);
_newObj.transform.parent = objs[0].transform;
objs[0] = _newObj;
}
objs[0].name = pathArray[i];
if (i == 0)
objs[1] = objs[0];
}
}
}
return objs;
}
}
最后是生成的例子
[SingletonPathAttribute("[Test]/MonoSingle")]
public class TestMonoSingletonComponent:MonoSingletonComponent<TestMonoSingletonComponent>
{
public override void OnInitSingleton()
{
base.OnInitSingleton();
Debug.Log("TestMonoSingletonComponent OnInit");
}
}
上述代码也是借鉴(chao)别人进行完善,努力奋斗