2.单例模式

2.创建型模式-单例模式


2.1 基础知识

学习难度:1

使用频率:4

总分:9

定义

单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点。

说人话

让这个类在整个程序中只有一个对象存在。

结构图

实现步骤

  • 单例类
  • 私有构造函数
  • 静态变量 instance
  • 静态方法 GetInstance 或只读属性 Instance 返回 instance
  • 选择实现单例的模式
  • 客户端使用 GetInstance 方法得到唯一对象
  • 想实现单例的类继承泛型单例类,将自身当做泛型传入

说明

实现单例的模式主要包括:饿汉模式、懒汉模式、双重锁模式。
其他如枚举模式、Holder 模式、静态函数初始化模式等不太常用,故不作讲解。Lazy<T> 模式倒是可以看看,性能比双重锁好一些,且不用自己手动实现。


2.2 模版代码

饿汉模式

类加载时就创建,线程安全,可能造成资源浪费。

public class EagerSingleton
{
    private static EagerSingleton instance = new EagerSingleton();

    private EagerSingleton()
    {
    }

    public static EagerSingleton GetInstance()
    {
        return instance;
    }
}

懒汉模式

类使用时才创建,线程不安全,不会造成资源浪费。

public class LazySingleton
{
    private static LazySingleton instance;

    private LazySingleton()
    {
    }

    public static LazySingleton GetInstance()
    {
        if (instance == null)
        {
            instance = new LazySingleton();
        }

        return instance;
    }
}

双重锁模式

类使用时才创建,线程安全,不会造成资源浪费。

public class DoubleCheckedLockingSingleton
{
    private static DoubleCheckedLockingSingleton instance;
    private static readonly object lockObject = new object();

    private DoubleCheckedLockingSingleton()
    {
    }

    public static DoubleCheckedLockingSingleton GetInstance()
    {
        // 第一次检查,如果实例已经创建,则直接返回
        if (instance == null)
        {
            lock (lockObject) // 使用锁来确保只有一个线程进入临界区
            {
                // 第二次检查,因为多个线程可以同时通过第一次检查
                if (instance == null)
                {
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }

        return instance;
    }
}

2.3 CSharp实践

实践需求

实现 饿汉模式 懒汉模式 双重锁模式 泛型单例 让要实现单例的类 通过继承泛型单例类时传入自身 来实现单例

饿汉模式

注意泛型单例构造函数要是保护的 否则子类继承时编译报错

public class EagerSingleton<T> where T : new()
{
    private static T instance = new T();

    protected EagerSingleton()
    {
    }

    public static T GetInstance()
    {
        return instance;
    }
}

双重锁模式

注意泛型单例构造函数要是保护的 否则子类继承时编译报错

public class LazySingleton<T> where T : new()
{
    private static T instance;

    protected LazySingleton()
    {
    }

    public static T GetInstance()
    {
        if (instance == null)
        {
            instance = new T();
        }

        return instance;
    }
}

双重锁模式

注意泛型单例构造函数要是保护的 否则子类继承时编译报错

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

    protected DoubleCheckedLockingSingleton()
    {
    }

    public static T GetInstance()
    {
        // 第一次检查,如果实例已经创建,则直接返回
        if (instance == null)
        {
            lock (lockObject) // 使用锁来确保只有一个线程进入临界区
            {
                // 第二次检查,因为多个线程可以同时通过第一次检查
                if (instance == null)
                {
                    instance = new T();
                }
            }
        }

        return instance;
    }
}

2.4 Unity实践

实践需求

实现继承Monobehavior的泛型单例类

MonoBehavior模式

继承MonoBehaviour类不会写构造函数 核心思想是假如没有单例就全局找 找不到创建对象添加脚本 Awake时检测

using UnityEngine;

public class MonoBehaviorSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;

    public static T GetInstance()
    {
        if (instance == null)
        {
            // 如果实例为空,则尝试在场景中查找具有类型 T 的对象
            instance = FindObjectOfType<T>();

            if (instance == null)
            {
                // 如果在场景中找不到该类型的对象,则创建一个新的游戏对象并将其作为单例
                GameObject gameObject = new GameObject();
                instance = gameObject.AddComponent<T>();
                gameObject.name = typeof(T).ToString();

                // 在场景加载时不销毁该游戏对象
                DontDestroyOnLoad(gameObject);
            }
        }

        // 返回单例对象
        return instance;
    }

    protected void Awake()
    {
        if (instance == null)
        {
            // 如果实例为空,将当前对象设置为单例并在场景加载时不销毁
            // 假如场景中有多个该脚本,那么总有一个会先进来instance==null
            instance = this as T;
            DontDestroyOnLoad(this.gameObject);
        }
        else
        {
            // 如果实例已存在,则销毁当前对象,以确保只有一个单例对象存在
            Destroy(this);
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏