【Unity知识点详解】Unity中泛型单例的使用,兼容WebGL

发布于:2025-04-22 ⋅ 阅读:(59) ⋅ 点赞:(0)

        今天来讲下Unity中泛型单例的使用,包含普通单例和继承MonoBehaviour的单例。重点是需要两种泛型单例兼容WebGL平台,话不多说直接开始。

泛型单例的设计目标

         作为泛型单例,需要实现以下几个目标:

  • 全局唯一,在程序的整个运行周期内有且只有一个实例。
  • 全局调用,这属于单例的特性,方便任意调用。
  • 继承即用,通过继承泛型单基类例即可实现单例特性

WebGL兼容问题

       由于泛型单例要防止外部创建,构造函数会被定义为private或protected,因而对象需要通过反射的方式来创建。由于WebGL采用的是AOT的编译方式(Ahead-of-Time Compilation,提前编译),所以对反射的使用有很多的限制。

泛型单例的实现方式

        普通泛型单例的实现,示例代码如下:

public abstract class Singleton<T> where T : Singleton<T>
{
    private static readonly Lazy<T> mLazyInstance = new Lazy<T>(Constructor, LazyThreadSafetyMode.ExecutionAndPublication);

    public static T Instance => mLazyInstance.Value;

    private static T Constructor()
    {
        ConstructorInfo constructor = typeof(T).GetConstructor(
            BindingFlags.Instance | BindingFlags.NonPublic,
            null, Type.EmptyTypes, null);

        if (constructor == null)
        {
            throw new Exception($"No private/protected constructor found for {typeof(T).Name}");
        }

        return (T)constructor.Invoke(null);
    }
}

        MonoBehaviour泛型单例的实现,示例代码如下:

public abstract class SingletonMonoBehaviour<T> : MonoBehaviour
    where T : SingletonMonoBehaviour<T>
{
    private static readonly Lazy<T> mLazyInstance = new Lazy<T>(Constructor, LazyThreadSafetyMode.ExecutionAndPublication);

    public static T Instance => mLazyInstance.Value;

    private static T Constructor()
    {
        string path = SingletonPath;
        GameObject aimGameObject = UtilCollection.GameObjectUtil.FindOrCreateGameObject(path);

        T instance = aimGameObject.GetComponent<T>();

        if (instance == null)
            instance = aimGameObject.AddComponent<T>();

        return instance;
    }

    /// <summary>防止重复动态创建</summary>
    private void Awake()
    {
        StartCoroutine(CheckRepeatCreate());
    }

    /// <summary>防止重复挂接脚本</summary>
    private void Reset()
    {
        StartCoroutine(CheckRepeatCreate());
    }

    private IEnumerator CheckRepeatCreate()
    {
        GameObject aimGameObject = GameObject.Find(SingletonPath);
        if (this.gameObject != aimGameObject)
        {
            yield return null;
            if (this != null)
                DestroyImmediate(this);
        }
        else if (this.gameObject == aimGameObject)
        {
            T[] components = aimGameObject.GetComponents<T>();
            if (components.Length > 1)
            {
                yield return null;
                if (this != null)
                    DestroyImmediate(this);
            }
        }
    }
}

        示例中用Lazy<T>类型作为实例类型,Lazy<T>可以通过ExecutionAndPublication参数确保多线程安全,在多线程情况下同样只会有一个实例。虽然WebGL是单线程,但为了保持代码一致性,所以不做区分。


网站公告

今日签到

点亮在社区的每一天
去签到