Unity中常用的三种单例模式基类

欢迎加入光光的奇妙冒险,我是你们的煎饼光子老师。

提到框架,大家可能会觉得很复杂,我也是有同感

但好在,也不是所有的框架都需要我们掌握的

但这期提到单例模式却是大家一定要掌握!!!

因为它真的非常重要!!!


这里我整理了三种开发中常用的单例模式基类,希望大家看完后能多敲几遍,把它变成自己的知识

(如果想要直接获取代码的话,可以直接免费下载在文章上方的相关资源)

注:后面的mono指的就是Unity中的MonoBehaviour类

第一种,不继承mono的单例基类(这种自然就无法挂载到GameObject上了)

最大的缺点就是它的构造函数还可以在外部调用,会破坏唯一性

但这一点也可以很好的规避,那就是不去调用它的构造函数(只要懂得单例模式的程序员都不会犯这个问题)

另一种解决方法就是使用反射相关的知识,请大家看第二段代码

/// <summary>
/// 单例模式基类
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseManager<T>where T: class,new ()
{
    private static T instance;
    //通过属性获取,任选其一
    public static T Instance
    {
        get
        {
            if (instance == null)
                instance = new T();
            return instance;
        }
    }
    
    //通过属性获取,任选其一
    public static T GetInstance()
    {
        if (instance == null)
            instance = new T();
        return instance;
    }
}

这种写法就可以规避上面提到的问题,但需要注意的是继承此类的类,必须要有一个自己的私有的无参构造函数 (后面的这段代码作为参考)

using System;
using System.Reflection;
using UnityEngine;

/// <summary>
/// 单例模式基类
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseMgr<T>where T: class
{
    //继承BaseMgr的子类需要自己创建一个私有的无参构造
    private static T instance;    
    //通过属性获取
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                //利用反射得到私有的无参构造函数 来用于对象的实例化
                Type type = typeof(T);
                ConstructorInfo info = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
                                                           null,
                                                           Type.EmptyTypes,
                                                           null);
                if (info != null)
                    instance = info.Invoke(null) as T;
                else
                    Debug.LogError("没有找到对应的无参构造函数");
            }
            return instance;               
        }
    }    
}
public class TestMgr : BaseMgr<TestMgr>
{    
    //不继承mono的单例还要声明一个私有的无参构造,为了不让外面调用
    private TestMgr() { }
    public void TestLog()
    {
        Debug.Log("测试");
    }
}

第二种,继承mono但需要自己进行挂载的单例基类

创建完脚本后还需要自己挂载到需要的GameObject上

using UnityEngine;
 
/// <summary>
/// 手动挂载的继承mono的泛型单例
/// </summary>
public class SingletonMono<T> : MonoBehaviour where T: SingletonMono<T>
{
    private static T instance; // 唯一实例
 
    // 向外提供
    public static T Instance 
    {
        get { return instance; }
    }    
 
    protected virtual void Awake()
    {
        // 要是不为空,表示已经存在一个单例 销毁
        if(instance != null)
        {
            Destroy(this.gameObject);
        }
        else
        {
            // 强转单例
            instance = (T)this;
        }
    }    
}

第三种,继承mono但可以自动进行挂载的单例基类

using UnityEngine;

/// <summary>
/// 自动挂载的单例模式基类
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonAutoMono<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                //动态创建物体
                GameObject obj = new GameObject();
                //得到T脚本类名 为对象改名 这样编辑器中观看更明确
                obj.name=typeof(T).Name;
                //动态添加脚本 记得为instance赋值
                instance=obj.AddComponent<T>();
                //过场景不移除 保证它在整个游戏生命周期中都存在
                DontDestroyOnLoad(obj);
            }
            return instance;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Blueberry124/article/details/142360043