SingletonKit單例原始碼閱讀學習

畅知發表於2024-06-17

閱讀學習QFramework中的SingletonKit原始碼.

Singleton 普通類的單例

作為最常用的單例模組,透過繼承單例泛型類來實現,需要私有構造;

 //使用第一種介面單例方式
    internal class Class2Singleton : Singleton<Class2Singleton>
    {
        //記錄被初始化的次數
        private static int mIndex = 0;
        
        //私有構造
        private Class2Singleton(){}

        public override void OnSingletonInit()
        {
            mIndex++;
            Log(mIndex.ToString());
        }

        public void Log(string content)
        {
            Debug.Log("Class2Singleton" + content);
        }
    }

看一下父類的程式碼內容,首先是ISingleton介面,方便對單例生命週期進行管理,留有給單例中資料進行初始化操作的方法。

/// <summary>
    /// 單例介面
    /// </summary>
    public interface ISingleton
    {
        /// <summary>
        /// 單例初始化(繼承當前介面的類都需要實現該方法)
        /// </summary>
        void OnSingletonInit();
    }

    /// <summary>
    /// 普通類的單例
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Singleton<T> : ISingleton where T : Singleton<T>
    {
        /// <summary>
        /// 靜態例項
        /// </summary>
        protected static T mInstance;

        /// <summary>
        /// 標籤鎖:確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區。
        /// 如果其他執行緒試圖進入鎖定的程式碼,則它將一直等待(即被阻止),直到該物件被釋放
        /// </summary>
        static object mLock = new object();

        /// <summary>
        /// 靜態屬性
        /// </summary>
        public static T Instance
        {
            get
            {
                lock (mLock)
                {
                    if (mInstance == null)
                    {
                        //呼叫單例構造器例項化單例物件
                        mInstance = SingletonCreator.CreateSingleton<T>();
                    }
                }

                return mInstance;
            }
        }

        /// <summary>
        /// 資源釋放
        /// </summary>
        public virtual void Dispose()
        {
            mInstance = null;
        }

        /// <summary>
        /// 單例初始化方法
        /// </summary>
        public virtual void OnSingletonInit()
        {
        }
    }

其中呼叫單例建構函式中相關內容:

 internal static class SingletonCreator
    {
     //透過反射來進行物件構造
        static T CreateNonPublicConstructorObject<T>() where T : class
        {
            var type = typeof(T);
            // 獲取私有建構函式
            var constructorInfos = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

            // 獲取無參建構函式
            var ctor = Array.Find(constructorInfos, c => c.GetParameters().Length == 0);

            if (ctor == null)
            {
                throw new Exception("Non-Public Constructor() not found! in " + type);
            }

            return ctor.Invoke(null) as T;
        }

        public static T CreateSingleton<T>() where T : class, ISingleton
        {
            var type = typeof(T);
            var monoBehaviourType = typeof(MonoBehaviour);

            if (monoBehaviourType.IsAssignableFrom(type))
            {
                return CreateMonoSingleton<T>();
            }
            else
            {
                var instance = CreateNonPublicConstructorObject<T>();
                instance.OnSingletonInit();
                return instance;
            }
        }
     //······
}

編寫案例呼叫單例;

 public class SingletonExample : MonoBehaviour
    {
        private void Start()
        {
            Class2Singleton.Instance.Log("我是第一種單例使用方式,第一次使用噢!");
            
            Class2Singleton.Instance.Dispose();
            
            //再次呼叫
            Class2Singleton.Instance.Log("我是第一種單例使用方式,銷燬之後,再次使用噢");
            
        }
    }

image-20240617170633493

MonoSingleton繼承Mono的單例

MonoSingleton是繼承Mono的單例型別,繼承MonoBehaviour類和ISingleton介面,其實現方式與Singleton的內容相似

 /// <summary>
    /// 靜態類:MonoBehaviour類的單例
    /// 泛型類:Where約束表示T型別必須繼承MonoSingleton<T>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class MonoSingleton<T> : MonoBehaviour, ISingleton where T : MonoSingleton<T>
    {
        /// <summary>
        /// 靜態例項
        /// </summary>
        protected static T mInstance;

        /// <summary>
        /// 靜態屬性:封裝相關例項物件
        /// </summary>
        public static T Instance
        {
            get
            {
                if (mInstance == null && !mOnApplicationQuit)
                {
                    mInstance = SingletonCreator.CreateMonoSingleton<T>();
                }

                return mInstance;
            }
        }

        /// <summary>
        /// 實現介面的單例初始化
        /// </summary>
        public virtual void OnSingletonInit()
        {
        }

        /// <summary>
        /// 資源釋放
        /// </summary>
        public virtual void Dispose()
        {
           
            if (SingletonCreator.IsUnitTestMode)
            {
                var curTrans = transform;
                 //清除單元測試建立的單例物件
                do
                {
                    var parent = curTrans.parent;
                    DestroyImmediate(curTrans.gameObject);
                    curTrans = parent;
                } while (curTrans != null);

                mInstance = null;
            }
            else
            {
                Destroy(gameObject);
            }
        }

        /// <summary>
        /// 當前應用程式是否結束 標籤
        /// </summary>
        protected static bool mOnApplicationQuit = false;

        /// <summary>
        /// 應用程式退出:釋放當前物件並銷燬相關GameObject
        /// </summary>
        protected virtual void OnApplicationQuit()
        {
            mOnApplicationQuit = true;
            if (mInstance == null) return;
            Destroy(mInstance.gameObject);
            mInstance = null;
        }

        /// <summary>
        /// 釋放當前物件
        /// </summary>
        protected virtual void OnDestroy()
        {
            mInstance = null;
        }

        /// <summary>
        /// 判斷當前應用程式是否退出
        /// </summary>
        public static bool IsApplicationQuit
        {
            get { return mOnApplicationQuit; }
        }
    }

來編寫案例來使用Mono單例

namespace QFramework.Example
{
    //使用MonoSingletonExample
    internal class Class2MonoSingleton : MonoSingleton<Class2MonoSingleton>
    {
        public override void OnSingletonInit()
        {
            Debug.Log(name + "---" + "OnSingletonInit");
        }

        private void Awake()
        {
            Debug.Log(name + "---" + "awake");
        }

        private void Start()
        {
            Debug.Log(name + "---" + "start");
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();
            
            Debug.Log(name + "---" + "OnDestroy");
        }
    }
    public class MonoSingletonExample : MonoBehaviour
    {
        private IEnumerator Start()
        {
            var instance = Class2MonoSingleton.Instance;

            yield return new WaitForSeconds(5.0f);
            
            instance.Dispose();
        }
        
    }
}

image-20240617175832663

5s後對單例物件進行釋放:

img

使用MonoSingletonProperty/SingletonProperty兩個工具類來封裝單例,在實現設定單例類時候不需要再繼承 MonoSingleton 或 Singleton ,只需要繼承ISingleton介面即可,當然,此處的介面作用就是對該類進行標記說說明,表明類是單例模式。下面看下兩個工具類的具體實現:

 /// <summary>
    /// 屬性單例類
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static class SingletonProperty<T> where T : class, ISingleton
    {
        /// <summary>
        /// 靜態例項
        /// </summary>
        private static T mInstance;

        /// <summary>
        /// 標籤鎖
        /// </summary>
        private static readonly object mLock = new object();

        /// <summary>
        /// 靜態屬性
        /// </summary>
        public static T Instance
        {
            get
            {
                lock (mLock)
                {
                    if (mInstance == null)
                    {
                        mInstance = SingletonCreator.CreateSingleton<T>();
                    }
                }

                return mInstance;
            }
        }

        /// <summary>
        /// 資源釋放
        /// </summary>
        public static void Dispose()
        {
            mInstance = null;
        }
    }

  /// <summary>
    /// 繼承Mono的屬性單例?
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static class MonoSingletonProperty<T> where T : MonoBehaviour, ISingleton
    {
        private static T mInstance;

        public static T Instance
        {
            get
            {
                if (null == mInstance)
                {
                    mInstance = SingletonCreator.CreateMonoSingleton<T>();
                }

                return mInstance;
            }
        }

        public static void Dispose()
        {
            if (SingletonCreator.IsUnitTestMode)
            {
                UnityEngine.Object.DestroyImmediate(mInstance.gameObject);
            }
            else
            {
                UnityEngine.Object.Destroy(mInstance.gameObject);
            }

            mInstance = null;
        }
    }


使用SignetonProperty來實現單例類:

 internal class Class2SingletonProperty : ISingleton
    {
        private static int mIndex = 0;
     
	   //靜態單例屬性
        public static Class2SingletonProperty Instance
        {
            get { return SingletonProperty<Class2SingletonProperty>.Instance; }
        }
        public void OnSingletonInit()
        {
            mIndex++;
        }

        private Class2SingletonProperty()
        {
        }

        public void Dispose()
        {
            SingletonProperty<Class2SingletonProperty>.Dispose();
        }

        public void Log(string content)
        {
            Debug.Log("Class2SingletonProperty" + mIndex + "---" + content);
        }

    }
    public class SingletonPropertyExample : MonoBehaviour
    {
        private void Start()
        {
            Class2SignetonProperty.Instance.Log("Hello Tony!");
            
            Class2SignetonProperty.Instance.Dispose();
            
            Class2SignetonProperty.Instance.Log("Hello TonyChang");
        }
    }

其執行結果和第一個單例實現案例結果相同。

使用MonoSingletonProperty實現單例類:

  
internal class Class2MonoSingletonProperty : MonoBehaviour, ISingleton
    {
       //僅僅需要宣告靜態屬性即可
        public static Class2MonoSingletonProperty Instance
        {
            get { return MonoSingletonProperty<Class2MonoSingletonProperty>.Instance; }
        }

       //繼承ISingleton 介面實現方法
        public void OnSingletonInit()
        {
            Debug.Log(name + "==" + "OnSingletonInit" );
        }

        public void Dispose()
        {
            MonoSingletonProperty<Class2MonoSingletonProperty>.Dispose();
        }

        private void Awake()
        {
            Debug.Log(name + "==" + "Awake" );
        }

        private void Start()
        {
            Debug.Log(name + "==" + "Start" );
        }

        private void OnDestroy()
        {
            Debug.Log(name + "==" + "OnDestroy" );
        }
    }
    public class MonoSingletonPropertyExample:MonoBehaviour
    {
        private IEnumerator Start()
        {
            var instance = Class2MonoSingletonProperty.Instance;

            yield return new WaitForSeconds(5.0f);
            
            instance.Dispose();
        }
    }

其執行結果和第二個繼承Mono的單例實現案例結果相同。

相關問題:

那麼不免產生疑問--使用MonoSingletonProperty/SingletonProperty兩個工具類來封裝單例好處是什麼?或者說解決什麼問題。

我們看不使用MonoSingletonProperty實現單例,需要繼承抽象類MonoSingleton,由於C#中只支援class單繼承,如果我想要此單例類再繼承一個Mono相關的類,那麼將增加很多的編碼工作。

image-20240617184607191

而使用MonoSingletonProperty之後是繼承介面,可以方便繼承Mono相關的類。

那麼使用ISingleton介面的作用是什麼?

作為泛型約束,也就是說想要成為單例的類必須要繼承ISingleton介面才可以使用MonoSingletonProperty/SingletonProperty兩個工具類。

變相的表明,想要加入單例圈子,需要繼承此介面來打個標籤。

image-20240617185704194

image-20240617185718773

UML圖

image-20240617201339124

原始碼地址:https://github.com/liangxiegame/SingletonKit

相關文章