Unity开发通用泛型单例模式

本文介绍了C#中三种不同的单例模式实现:简单单例、线程安全单例以及针对继承MonoBehaviour的单例模式。每种实现方式都详细说明了其代码逻辑,并确保了单例的唯一性和线程安全性。此外,还强调了单例模式在防止多次实例化和共享全局状态时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、普通的C#单例模式(简单)

实现了一个简单的泛型单例模式。这个实现提供了一种方式,可以确保对于任何具有无参数构造函数的类型 T,该类型仅会存在一个实例,并提供一个全局访问点。
简单易用,但缺乏线程安全保护。通过简单的修改,可以实现一个更健壮的单例模式,使其适应多线程环境。在设计模式的使用中要考虑到用例的需求,并根据实际情况进行调整。

public class Singleton<T> where T :new()  {
	private static T m_Instance;

	public static T Instance {
		get {
			if (m_Instance == null) {
				m_Instance = new T();
			}
			return m_Instance;
		}
	}
}

一个基本的单例模式的实现,它利用了延迟初始化(lazy initialization)来创建实例。尽管这个实现比较简单、有效,但在多线程环境中可能会出现潜在的线程安全问题。在竞争条件的情况下,多个线程可能会同时检测到 m_Instance 为 null,并同时创建多个实例。
改进后的版本
使用lock关键字来确保在多线程场景下的安全性。以下是改进后的代码示例:

public class Singleton<T> where T : new()  
{  
    private static T m_Instance;  
    private static readonly object m_Lock = new object();  

    public static T Instance  
    {  
        get  
        {  
            // 使用双重检查锁定(Double-check locking)以避免不必要的锁定  
            if (m_Instance == null)  
            {  
                lock (m_Lock)  
                {  
                    if (m_Instance == null)  
                    {  
                        m_Instance = new T();  
                    }  
                }  
            }  
            return m_Instance;  
        }  
    }  
}

代码解析:

  1. 锁对象:创建一个私有的静态锁对象 m_Lock,在访问实例时使用它来确保线程互斥。
  2. 双重检查锁定:在获取实例前,先检查 m_Instance 是否为 null。若还未实例化,再进入锁定区块。同时,在锁定区块内再次检查 m_Instance 是否为 null,以避免在锁内创建多个实例的情况。

使用 Lazy 的更优解法
当然,如前面提到的,使用 Lazy 可以进一步简化代码,并确保线程安全,避免手动管理锁:

public class Singleton<T> where T : new()  
{  
    private static readonly Lazy<T> m_Instance = new Lazy<T>(() => new T());  

    public static T Instance => m_Instance.Value;  
}

为什么选择使用 Lazy?
简洁性:代码更简洁,易于维护。
线程安全:Lazy 内置了线程安全的实现,避免了显式的锁管理。
延迟初始化:实例只有在第一次访问时才会被创建,节省了资源。
总结
如果您需要一个简单的单例实现,并且考虑到线程安全,以上的双重检查锁定方法是合适的。如果您对简便性、可读性和线程安全的保证有更高的要求,推荐使用 Lazy。在大多数情况下,Lazy 是实现单例模式的最佳实践。
为了进一步优化单例模式的实现,我们可以考虑使用静态初始化器或者懒汉式与双重检查锁定的结合,但通常静态初始化器是实现单例模式的最佳方式,因为它既简单又高效,并且由CLR(公共语言运行时)保证了线程安全。

public class Singleton<T> where T : new()  
{  
    // 静态初始化器在类被加载时立即初始化,保证线程安全  
    private static readonly T m_Instance = new T();  
  
    // 提供一个公共的静态属性来访问实例  
    public static T Instance  
    {  
        get { return m_Instance; }  
    }  
  
    // 私有构造函数,防止外部实例化  
    private Singleton() {}  
}

二、普通的C#单例模式(多线程安全机制)

一个典型的泛型单例模式(Singleton Pattern)的实现,它确保某个类只会有一个实例,并提供一个全局访问点来获得该实例。这个泛型单例模式的实现是一个有效且线程安全的单例设计,它遵循了 C# 的最佳实践。此模式的主要优点是能够抽象出单例的逻辑,使得任何希望成为单例的类都可以通过简单的继承来实现。
一个基本的单例模式实现,并且包含了多线程安全机制。虽然这个实现已经很接近理想,但在某些方面还有进一步优化的空间。此外,类的设计允许您创建任意类型的单例(只要这些类型符合约束条件)

public abstract class Singleton<T> where T : class, new()
{
    private static T instance = null;
 
    // 多线程安全机制
    private static readonly object locker = new object();
 
    public static T Instance
    {
        get
        {
            lock (locker)
            {
                if (instance == null)
                    instance = new T();
                return instance;
            }
        }
    }
}

改进建议
虽然您的实现已经很不错,但使用双重检查锁定(Double-check locking)是不必要的加锁操作,可以提升性能。以下是改进后的代码示例:

public abstract class Singleton<T> where T : class, new()  
{  
    private static T _instance = null;  
    private static readonly object _locker = new object();  
    
    public static T Instance  
    {  
        get  
        {  
            if (_instance == null) // 首次检查  
            {  
                lock (_locker) // 锁定  
                {  
                    // 再次检查  
                    if (_instance == null)  
                    {  
                        _instance = new T(); // 实例化  
                    }  
                }  
            }  
            return _instance;  
        }  
    }  
}

如果你想避免锁定和提高标准化程度,可以考虑使用 Lazy 实现更简洁的单例模式:

using System;  

public class Singleton<T> where T : class, new()  
{  
    private static readonly Lazy<T> instance = new Lazy<T>(() => new T());  

    public static T Instance => instance.Value;  

    // 构造函数被设为 protected,防止外部实例化  
    protected Singleton() { }  
}

进一步的建议
如果你想避免锁定和提高标准化程度,可以考虑使用 Lazy 实现更简洁的单例模式:

public abstract class Singleton<T> where T : class, new()  
{  
    private static readonly Lazy<T> _instance = new Lazy<T>(() => new T());  
    
    public static T Instance => _instance.Value;  
}

小结
您的初始实现是有效和合理的,但使用双重检查锁定可以提高性能。
使用 Lazy 是一个现代且简洁的解决方案,它同样能提供线程安全和懒加载的好处,避免手动管理锁或额外的条件验证。
通过以上的改进和建议,您可以更好地实现单例模式,增强代码的性能和可读性。

三、单例模式(继承需要一个非公有的无参构造函数)

实现了一个带有线程安全机制的单例模式。与前一个实现相比,这里的 Singleton2 使用了反射来创建实例,这样就允许了使用非公共构造函数的类。以下是代码的详细分析及其优缺点。
此实现通过反射创建单例实例的方式提供了很大的灵活性,但同时也带来了复杂性和性能上的开销。在设计和使用单例模式时,应权衡利弊,根据实际需求选择适合的实现方式。
改进保留了反射的单例实现,并加强了代码的可读性和可维护性,同时避免了一些潜在问题。确保单例模式的实现功能强大而又不失简单明了,可以有效地帮助开发者在项目中使用单例模式。

/// <summary>
/// 单例
/// 继承需要一个非公有的无参构造函数
/// </summary>
/// <typeparam name="T">类名的泛型</typeparam>
public abstract class Singleton2<T> where T : class
{
    private static T instance = null;
    // 多线程安全机制
    private static readonly object locker = new object();

    public static T Instance
    {
        get
        {
            // 线程锁
            lock (locker)
            {
                if (null == instance)
                {
                    // 反射获取实例
                    var octors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

                    // 获取无参数的非公共构造函数
                    var octor = Array.Find(octors, c => c.GetParameters().Length == 0);

                    // 没有则提示没有私有的构造函数
                    if (null == octor)
                    {
                        throw new Exception("No NonPublic constructor without 0 parameter");
                    }

                    // 实例化
                    instance = octor.Invoke(null) as T;
                }

                return instance;

            }
        }
    }

    /// <summary>
    /// 构造函数
    /// 避免外界new
    /// </summary>
    protected Singleton2() { }
}

改进后的版本

using System;  
using System.Linq;  
using System.Reflection;  

public abstract class Singleton2<T> where T : class  
{  
    private static T instance;  
    private static readonly object locker = new object();  

    public static T Instance  
    {  
        get  
        {  
            // 线程锁  
            if (instance == null)  
            {  
                lock (locker)  
                {  
                    if (instance == null)  
                    {  
                        // 反射获取实例  
                        var ctor = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)  
                                             .FirstOrDefault(c => c.GetParameters().Length == 0);  

                        // 没有则提示没有私有的构造函数  
                        if (ctor == null)  
                        {  
                            throw new InvalidOperationException($"No non-public constructor without parameters found for type {typeof(T)}.");  
                        }  

                        // 实例化  
                        instance = ctor.Invoke(null) as T;  
                    }  
                }  
            }  
            return instance;  
        }  
    }  

    // 构造函数  
    protected Singleton2() { }  
}

四、继承MonoBehaviour的单例模式(一)

一个特定于 Unity 的单例模式实现,确保在场景中只有一个派生自 MonoBehaviour 的类实例。
这个模式为在 Unity 中创建单实例的 MonoBehaviour 脚本提供了一种简便的方法,同时确保在任何时候只有一个实例存在。通过所建议的改进,你可以确保单例的健壮性并优雅地处理潜在问题。

using UnityEngine;
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {

                _instance = FindObjectOfType<T>();
                //if (FindObjectOfType<T>() > 1)
                //{
                //    return FindObjectOfType<T>;
                //}
                if (_instance == null)
                {
                    _instance = new GameObject(typeof(T).ToString()).AddComponent<T>();
                }
            }
            return _instance;
        }
    }
}

稍微改进后的 MonoSingleton 版本

using UnityEngine;  

public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>  
{  
    private static T _instance;  

    public static T Instance  
    {  
        get  
        {  
         	// 如果实例未创建,自动创建一个  
            if (_instance == null)  
            {  
                _instance = FindObjectOfType<T>();  
                
                if (FindObjectsOfType<T>().Length > 1)  
                {  
                    Debug.LogError($"发现多个 {typeof(T)} 实例。确保单个实例。");  
                    return _instance; // 返回第一个找到的实例  
                }  

                if (_instance == null)  
                {  
                    _instance = new GameObject(typeof(T).Name).AddComponent<T>();  
                }  
            }  
            return _instance;  
        }  
    }  

    // 可选:Unity 生命周期  
    protected virtual void Awake()  
    {  
        if (_instance == null)  
        {  
            _instance = this as T;  
        }  
        else if (_instance != this)  
        {  
            Debug.LogWarning($"实例 {typeof(T)} 已存在,正在销毁该实例。");  
            Destroy(gameObject);  
        }  
    }  

    // 可选:防止单例被销毁  
    protected virtual void OnDestroy()  
    {  
        if (_instance == this)  
        {  
            _instance = null;  
        }  
    }  
}

改进后的版本

using UnityEngine;  

public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>  
{  
    private static T _instance;  

    public static T Instance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                // 查找当前场景中是否有该单例的实例  
                _instance = FindObjectOfType<T>();  

                // 如果没有实例,则创建一个新的游戏对象并添加组件  
                if (_instance == null)  
                {  
                    GameObject singletonObject = new GameObject(typeof(T).Name);  
                    _instance = singletonObject.AddComponent<T>();  
                    // 设置此对象为不被销毁  
                    DontDestroyOnLoad(singletonObject);  
                }  
                else  
                {  
                    Debug.LogWarning($"Found existing instance of {typeof(T).Name}. Using it instead.");  
                }  
            }  
            return _instance;  
        }  
    }  

    protected virtual void Awake()  
    {  
        // 确保之前没有实例  
        if (_instance == null)  
        {  
            _instance = this as T;  
            DontDestroyOnLoad(gameObject);  
        }  
        else if (_instance != this)  
        {  
            Debug.LogError($"Another instance of {typeof(T).Name} exists! Destroying this duplicate.");  
            Destroy(gameObject);  
        }  
    }  
}

五、继承MonoBehaviour的单例模式(二)

一个通用的单例模式实现,在 Unity 中确保某个类型的 MonoBehaviour 仅有一个实例。这个 MonoSingleton 类是一个极具用处的模式实现,保证在游戏中只有一个特定类型的 MonoBehaviour 实例。通过实现上述改进,我们能够提高其健壮性,确保在使用中的合理性。

using UnityEngine;

public class MonoSingleton<T> : MonoBehaviour where T :MonoSingleton<T> {

	protected static T instance;

	public static T Instance
	{
		get { return instance; }
	}

	protected virtual void Awake()
	{
		if (instance == null)
		{
			instance = (T)this;
		}
		else
		{
			Debug.LogError("Get a second instance of this class" + this.GetType());
		}
	}
}

改进后的版本

using UnityEngine;  

public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>  
{  
    private static T _instance;  

    public static T Instance  
    {  
        get  
        {  
            // 如果实例为空,则尝试找到或创建一个  
            if (_instance == null)  
            {  
                _instance = FindObjectOfType<T>();  

                // 检查是否找到了多个实例  
                if (FindObjectsOfType<T>().Length > 1)  
                {  
                    Debug.LogError($"发现多个 {typeof(T)} 实例。确保只存在一个实例。");  
                    return _instance; // 返回第一个找到的实例  
                }  

                if (_instance == null)  
                {  
                    // 创建新的游戏对象并添加该单例  
                    GameObject singletonObject = new GameObject(typeof(T).Name);  
                    _instance = singletonObject.AddComponent<T>();  
                    DontDestroyOnLoad(singletonObject); // 不在场景切换中销毁  
                }  
            }  

            return _instance;  
        }  
    }  

    protected virtual void Awake()  
    {  
        // 进行单例注册  
        if (_instance == null)  
        {  
            _instance = this as T;  
        }  
        else if (_instance != this)  
        {  
            Debug.LogWarning($"实例 {typeof(T)} 已存在,正在销毁当前实例。");  
            Destroy(gameObject); // 销毁多余的实例  
        }  
    }  

    protected virtual void OnDestroy()  
    {  
        // 在销毁时清空实例引用  
        if (_instance == this)  
        {  
            _instance = null;  
        }  
    }  
}

总结
这个改进的 MonoSingleton 设计提供了完整的单例访问和安全机制,采用正确的方法管理 Unity 的 MonoBehaviour 组件,使得使用该类的派生类在游戏中仅能有唯一实例。通过这些改进,代码可以更安全、容易使用且方便扩展。

using UnityEngine;  

public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>  
{  
    protected static T instance;  
    private static readonly object lockObject = new object();  

    public static T Instance  
    {  
        get  
        {  
            // 如果实例已经被赋值,直接返回  
            if (instance != null) return instance;  

            lock (lockObject) // 锁定以确保线程安全  
            {  
                if (instance == null)  
                {  
                    // 尝试查找现有对象  
                    instance = FindObjectOfType<T>();  

                    // 如果没有现有对象就进行创建  
                    if (instance == null)  
                    {  
                        GameObject singletonObject = new GameObject(typeof(T).Name);  
                        instance = singletonObject.AddComponent<T>();  
                        DontDestroyOnLoad(singletonObject); // 确保在场景加载时不被销毁  
                    }  
                }  

                return instance;  
            }  
        }  
    }  

    protected virtual void Awake()  
    {  
        if (instance == null)  
        {  
            instance = (T)this;  
        }  
        else if (instance != this)  
        {  
            Debug.LogError($"Duplicate instance of {this.GetType()} detected. Destroying this instance.");  
            Destroy(this.gameObject); // 销毁冗余实例  
        }  
    }  

    // 额外的可选方法:手动创建单例(如果需要)  
    public static void CreateInstance()  
    {  
        if (instance != null)  
        {  
            Debug.LogWarning($"Instance of {typeof(T)} already exists!");  
            return;  
        }  

        GameObject singletonObject = new GameObject(typeof(T).Name);  
        instance = singletonObject.AddComponent<T>();  
        DontDestroyOnLoad(singletonObject);  
    }  
}

六、继承MonoBehaviour的单例模式(三)

是一个 MonoBehaviour 类型的单例实现,其设计目的是确保在同类对象中只有一个实例。这个基于 Unity 的单例模式实现可以有效管理单个对象实例。通过上述改进,增强了其健壮性并提供了更友好的使用体验,尤其是在复杂场景或多场景管理的情况下。这种单例模式对于常用的管理对象(如游戏管理器、音效管理器等)非常实用。

/// <summary>
/// 单例[继承MonoBehaviour带Awake]
/// </summary>
public class MonoSingleton2<T> : MonoBehaviour where T : MonoSingleton2<T>
{

    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
                Debug.LogError(typeof(T).ToString() + " is NULL.");
            return _instance;
        }
    }

    private void Awake()
    {
        if (_instance != null)
        {
            //one more Initialize
            Debug.LogError(typeof(T).ToString() + " aready have one on" + _instance.gameObject.name);
        }
        _instance = this as T;//or  _instance = (T)this; 
        //call Init
        Init();
    }

    /// <summary>
    /// 需要在awake初始化的请重载Init
    /// </summary>
    protected virtual void Init()
    {
        //optional to override
    }
}

改进后的版本

using UnityEngine;  

public class MonoSingleton2<T> : MonoBehaviour where T : MonoSingleton2<T>  
{  
    private static T _instance;  

    public static T Instance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                Debug.LogError($"{typeof(T)} is NULL. Instance hasn't been created yet.");  
            }  
            return _instance;  
        }  
    }  

    protected virtual void Awake()  
    {  
        if (_instance != null && _instance != this)  
        {  
            Debug.LogError($"{typeof(T)} already exists on {_instance.gameObject.name}. Destroying this instance.");  
            Destroy(gameObject); //销毁多于的实例  
            return; // 防止后续代码执行  
        }  

        _instance = this as T; // 或 _instance = (T)this;  
        DontDestroyOnLoad(gameObject); // 保持单例在场景间不被销毁  
        Init(); // 调用初始化方法  
    }  

    /// <summary>  
    /// 需要在 Awake 中初始化的请重载 Init  
    /// </summary>  
    protected virtual void Init()  
    {  
        // 可选重载  
    }  
}

参考博客地址如下
1、https://www.cnblogs.com/koshio0219/p/11203631.html
2、https://www.codetd.com/article/11599698
3、https://blog.csdn.net/u014361280/article/details/103868778

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值