? ? ? ? 今天來講下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> : MonoBehaviourwhere 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是單線程,但為了保持代碼一致性,所以不做區分。