C#设计模式——单例模式(Singleton Pattern)

2023-11-04

目录

一、概述

1)基础

二、实现

1)单线程

2)解决——多线程情况下

解决方案一:Sleep

解决方案二:加锁

三、扩展


一、概述

单例模式->创建型设计模式

定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。(在第一个使用者创建了这个类的实例之后,其后需要使用这个类就只能使用之前创建的实例,无法再创建一个新的实例。)

1)基础

        “确保一个类只有一个实例” + “提供一个访问它的全局访问点

        首先,我们再创建类的实例会想到用什么方式来创建?new。

        那么为什么可以通过它来创建一个实例?因为构造函数,若未在类中定义构造函数,则编译器会自动帮我们生成一个无参的构造函数。如果类定义私有的构造函数就不能在外界通过new创建实例了。【故,确保一个类只有一个实例,利用“定义私有构造函数”+“定义一个保存该实例的静态私有变量”(为什么定义为静态?静态是为了在多线程中确保只有一个实例)】

        其次,关于提供实例的全局访问点?可以定义一个公有方法或者属性来把该类的实例公开出去。

二、实现

1)单线程

通过索引器实现:目标实例只可存在一个

  • 实现原理:get中判断目标实例是否为空,为空则new一个,反则不进行操作,返回一个存在的实例。
  • 索引器小知识get 获取属性值, set 设置属性值。

  • public class Prpgram
    {
        static void Main(String[] args)
        {
            PrA a = PrA.Intance;    //对象1
            PrA b = PrA.Intance;    //对象2
            Console.Read();
        }
        ///实例类
        public class PrA
        {
            //定义一个PrA类型的静态变量来保存类的实例
            private static PrA intance = null;
            //定义公有属性来提供全局访问点
            public static PrA Intance
            {
                get
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    {
                        intance = new PrA();
                    }
                    return intance;
                }
            }
            //定义私有构造函数,使外界不能创建该类实例
            private PrA()
            {
                Console.WriteLine("实例化!");
            }
        }
    }
  • 结果

     结论:单例模式下,任何地方对单例类创建对象,程序最多只存在一个实例。

        (字段命名规范,首字母大写的是给外部使用,小写的给内部使用。)

  • 注意:以上只适合单线程情况

    • 多线程情况下产生问题
    • 结果:多次实例化
    • 解释

      • 打印多个?因为同一个时间片完成n个线程的输出,即同时完成对intance字段的get获取,皆为null时访问,故将访问n次实例化n个对象。
      • for循环20次却只产生小于20个的对象?线程创建需要时间,而Mian方法中代码执行速度很快,即有可能执行至程序结束时部分线程还未创建完成。

2)解决——多线程情况下

解决方案一:Sleep

  • 实现:在for中利用Sleep解决,达到线程非同时进行。
  • 存在问题:多线程之间切记不使用Sleep直接停止线程。(这里不详细介绍)

解决方案二:加锁

  • 实现对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在
  • 优点:多线程之间,线程安全的一种做法。
  • 缺点这个版本加上了一个对instance的锁,在调用instance之前要先对objlock上锁,这样就避免了实现一中的线程冲突,该实现自始至终只会创建一个instance了。但是,由于每次调用Instance都会使用到锁,而调用锁的开销较大,这个实现会有一定的性能损失。
  • for (int i = 0; i < 20; i++)
    {
        Thread th = new Thread(x =>
        {
            PrA pa = PrA.Intance;   //创建了很多对象,很多单例模式
        });
        th.IsBackground = true;
        th.Start();
        }
        /*Thread.Sleep(100);    //方案一:可解决该方式下多线程多实例化的情况,存在问题 */
    }
    /*方案二:加锁
    ...
        private static object objlock = new object();
        public static PrA Intance
        {
            get
            {
                lock (objlock)    //加锁
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    { 
                        intance = new PrA(); 
                    }
                    return intance;
                }
    
            }
        }
        //定义私有构造函数,使外界不能创建该类实例
        private PrA()
        {
             Console.WriteLine("实例化!");
        }
    ...
    */
  • 改进

        利用双层加锁


...
    // 双重锁定只需要一句判断就可以了
    if (intance is null)
    {
        lock (objlock)    //加锁
        {
            if (intance is null)    //实例是否存在
            { 
                intance = new PrA(); 
            }
            return intance;
        }
    }
...

三、扩展

先看以下代码 

public class Prpgram
{
    static void Main(String[] args)
    {
        List<PrA> list = new List<PrA>();

        for (int i = 0; i < 20; i++)
        {
            Thread th = new Thread(x =>
            {
                PrA pa = PrA.Intance;   //创建了很多对象
                list.Add(pa);
            });
            th.IsBackground = true;
            th.Start();
        }
        Thread.Sleep(500); //防止执行至程序结束时线程还未创建完成
        list[0].list_ss.Add(new ss() { name = "aa", age = 123 });   //给第一添加数据
        list[0].id = 1;
        list[6].id = 2;
        foreach (var item in list)
        {
            if (item.list_ss.Count > 0)
            {
                Console.WriteLine($"{item.list_ss[0].name}-{item.list_ss[0].age}-{item.id}");
            }
        }
        Console.Read();
    }

    ///实例类
    public class PrA
    {
        //静态PrA类型的字段
        private static PrA intance = null;
        private static object objlock = new object();
        public static PrA Intance
        {
            get
            {
                lock (objlock)
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    { intance = new PrA(); }
                    return intance;
                }

            }
        }
        //构造函数
        private PrA()
        { Console.WriteLine("实例化!"); }

        public List<ss> list_ss = new();
        public int id = 0;
    }

    public class ss
    {
        public string name { get; set; }
        public int age { get; set; }
    }
}

借鉴大佬文章:https://www.cnblogs.com/zhili/p/SingletonPatterm.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#设计模式——单例模式(Singleton Pattern) 的相关文章

  • 到底什么是“位填充”或“填充位”?

    我只是在互联网上找不到任何关于 位填充 真正含义的详细解释 并且在 Stack Overflow 上也没有找到与位填充相关的线程的任何答案 我还搜索了 ISO 9899 1990 其中提到了 位填充 但没有根据我的需要进行解释 我在网上找到
  • 如何使用 saxon 将文档类型参数传递给 xslt?

    对于发送原子数据类型将使用类似 transformer SetParameter new QName customXml new XdmAtomicValue true 如何将 XML Node 作为参数从 C 传递给 XSLT 你能帮我么
  • 根据另一个列表的内容对列表进行排序

    我有一个包含整数列表的列表和另一个包含同时包含整数和字符串的类的列表 我想做的是按字母顺序对列表进行排序 将第一个列表中存在的条目放在前面 这是我的代码和预期输出 using System using System Collections
  • 模拟 EF core dbcontext 和 dbset

    我正在使用 ASP NET Core 2 2 EF Core 和 MOQ 当我运行测试时 我收到此错误 消息 System NotSupportedException 非虚拟 可在 VB 中重写 成员上的设置无效 x gt x Movies
  • EASTL 与 STL 相比,std::vector::operator[] 怎么会有这么大的性能差异

    根据http www open std org jtc1 sc22 wg21 docs papers 2007 n2271 html http www open std org jtc1 sc22 wg21 docs papers 2007
  • DLL 需要访问其应用程序的符号

    在 C 中 DLL 是否可以访问加载它的应用程序的某些符号 我有一个加载插件 dll 的应用程序 这些插件需要访问该应用程序的某些API 是否可以在不创建共享此 API 的新 DLL 的情况下实现此目的 函数指针结构适合这种情况吗 示例 主
  • 输入缓冲区刷新

    考虑下面的代码 include
  • 使用 microsoft word.interop 删除 Word 文档中的空白页

    我创建了一个Word文档 它使用以下命令生成动态内容词互操作 它有一些分页符之间使用 我面临的问题是 此分页符会创建我不想向用户显示的空白页面 在某些情况下 我需要在那里添加这些分页符以维护页面布局 因此我无法考虑删除这些分页符 但我想要的
  • 如何从c++调用python

    我是Python新手 我尝试像这样从 C 调用 python 脚本 在 Raspberry Pi 中 std string pythonCommand python Callee py a b int res system pythonCo
  • 将 libpng 链接到 android 原生项目

    我在尝试在本机 Android 项目中加载 libpng 时遇到问题 编译器似乎无法识别 libpng 函数 但可以识别类型 如 png byte 它可以正常编译类型 但如果我添加函数 则会抛出错误 这是编译输出 Windows 7 cmd
  • 在 C# 4.0 中,是否可以从泛型类型参数派生类?

    我一直在尝试这个 但我似乎无法弄清楚 我想做这个 public abstract class SingletonType
  • “sizeof”对不完整类型列表结构 C 的无效应用

    我正在尝试实现一种处理页面错误的替换算法 因此 我尝试使用 malloc 创建一个循环链表 但出现以下错误 无效的应用程序sizeof to incomplete typepageInMemory 以下是代码 typedef struct
  • Facebook Graph API“/userid/feed”返回空白

    我正在使用 Facebook C SDK 但似乎无法使用 Graph API 获取反馈数据 我已从用户那里获得了以下扩展权限 范围 离线访问 publish stream publish checkins create event read
  • 是否可以从.NET Core中间件检索控制器的操作结果?

    public class UsersController APIControllerBase public UsersController public Client Get return new Client ClientID 1 Las
  • STL 向量、迭代器和插入 (C++)

    我有一个将向量的迭代器传递到的方法 在这个方法中 我想向向量中添加一些元素 但我不确定当只有迭代器时这是否可行 void GUIComponentText AddAttributes vector
  • 为 C++ 类播种 rand()

    我正在开发一个 C 类 它使用rand 在构造函数中 我真的希望这个班级在几乎所有方面都能照顾好自己 但我不知道在哪里播种rand 如果我播种rand 在构造函数中 每次构造我的对象类型的新实例时都会对其进行播种 因此 如果我按顺序创建 3
  • C中的pipe()和fork()

    我需要创建两个子进程 一个子进程需要运行命令 ls al 并将其输出重定向到下一个子进程的输入 而下一个子进程又将对其输入数据运行命令 sort r n k 5 最后 父进程需要读取该数据 已排序的数据 并将其显示在终端中 终端中的最终结果
  • Python 中的 C 指针算术

    我正在尝试将一个简单的 C 程序转换为 Python 但由于我对 C 和 Python 都一无所知 这对我来说很困难 我被 C 指针困住了 有一个函数采用 unsigned long int 指针并将其值添加到 while 循环中的某些变量
  • Android NDK - 仅用 C/C++ 编写

    有没有一种可能的方法可以使用 C C 编写整个 NDK 应用程序 而无需像 hello jni 示例项目 HelloJni java 中那样的 Java 入门 类 以某种方式创建一个 HelloJni c 来执行相同的操作 从 Androi
  • GetActiveObject() 与 GetObject() -- MK_E_UNAVAILABLE 错误

    All 我在将一些 VBA 代码转换为 C 时遇到一些问题 我们有一个充当本地 COM 服务器的第 3 方应用程序 在我们使用的VBA代码中获取对象 获取对现有对象的引用 e g Set appHandle GetObject ProgId

随机推荐

  • bash_profile和.bashrc的区别

    1 etc profile 此文件为系统的每个用户设置环境信息 当用户第一次登录时 该文件被执行 并从 etc profile d目录的配置文件中搜集shell的设置 2 etc bashrc 为每一个运行bash shell的用户执行此文
  • Java中关于thread的停止问题

    stop Deprecated public final void stop Throwable obj 已过时 该方法具有固有的不安全性 请参阅 stop 以获得详细信息 该方法的附加危险是它可用于生成目标线程未准备处理的异常 包括若没有
  • DES加密Delphi、C#互通(CBC加密模式)

    Delphi 目录 https blog csdn net dkbnull article details 87935698 unit Unit1 interface uses Windows Classes SysUtils Dialog
  • Docker实战-编写Dockerfile

    一 编译镜像 1 编译镜像 Dockerfile类似于Makfile 用户使用docker build就可以编译镜像 使用该命令可以设置编译镜像时使用的CPU数量 内存大小 文件路径等 语法 docker build OPTIONS PAT
  • 初学Spring框架

    Spring 是一个java 项目开发的框架技术 所谓框架可以看成一个项目的半成品 已经具备了一个项目项目的基本骨架部分 需要自己实现一些具体的内容 Spring官网 初学Spring框架 Spring Framework 简介 IoC 入
  • Unity实战篇:讨论动画过程和计算伤害之间的关系

    在开发游戏的时候 我们会遇到这样的需求 当人物的攻击落到敌人身上时 播放特效 声效 产生伤害 等等一系列要求 那么我们要怎么实现呢 先了解一下怎么添加动画帧事件 https blog csdn net qq 15020543 article
  • Matlab学习5-图像处理之图像乘法、除法、边缘检测

    Matlab学习5 图像处理之逻辑运算 图像乘法 除法 边缘检测 1 图像乘法 效果 代码 图像相乘 img1 imread img rice png img2 imread img F4 11b MASK bmp img3 immulti
  • 【Getting Started with LLVM Core Libraries】P30 3.4 实验 使用独立工具

    LLVM学习笔记 Getting Started with LLVM Core Libraries P30 3 4 实验 使用独立工具 我们来看一个由分散在多个源文件中的函数组成的简单的C程序 但是 我们使用独立工具也可以获得相同的结果 为
  • 重定向爬虫和多线程爬虫

    前言 重定向爬虫是指在抓取网页时 如果目标网站内部存在重定向机制 即当你访问一个网页时 服务器会把你重定向到另一个目标网页 重定向爬虫可以帮助我们发现这种重定向链接 从而更有效地抓取目标网站的内容 要实现重定向爬虫 你需要在爬虫代码中添加重
  • 【云原生进阶之PaaS中间件】第一章Redis-1.2数据类型

    1 Redis 数据类型 Redis支持五种数据类型 string 字符串 hash 哈希 list 列表 set 集合 及zset sorted set 有序集合 1 1 String 字符串 string是redis最基本的类型 你可以
  • GridView横向滚动

    GridView和ListView都是android比较重要的控件 但是横滚的控件不是太多 这里介绍怎么把GridView横向滚动起来 看到其他网友也有相应的解决方法 自己只是把这些知识总结一下 供大家参考 首先让GridView横向滚动需
  • MySQL慢查询优化

    目录 优化方式 sql语句优化 索引优化 explain分析执行计划 show profile分析SQL trace分析优化器执行计划 通过慢查询日志定位那些执行效率低的SQL语句 MySQL大分页查询问题 一文详解MySQL各种锁及MVC
  • Android获取assets子目录注意事项

    获取assets子目录方法 span style font size 14px String subList getAssets list subdir span 返回当前目录下的所有文件 夹名 但不包含当前目录下子目录的文件 夹名 如果需
  • react源码中的hooks

    今天 让我们一起深入探究 React Hook 的实现方法 以便更好的理解它 但是 它的各种神奇特性的不足是 一旦出现问题 调试非常困难 这是由于它的背后是由复杂的堆栈追踪 stack trace 支持的 因此 通过深入学习 React 的
  • nacos的使用(借鉴淘宝Diamond配置中心)

    nacos常见的使用方法无非就是将获取到的配置文件放入propertise文件中 再从propertise中获取内容 如果配置过多 每增加一个nacos配置就得加一个propertise文件 文件多了 不便于维护 像一些需要经常改动的配置
  • softmax(二):softmax交叉熵不是真正的目标函数

    最近一直在消化 吸收大神的输出 在这里主要把思路捋顺 试着增加自己的理解和扩展 首先 上链接 还有我的膝盖 拜王峰大神 可以直接看大神的文字 从最优化的角度看待Softmax损失函数 Softmax理解之Smooth程度控制 一 优化的目标
  • 常用的设计模式

    单例模式 简单点说 就是一个应用程序中 某个类的实例对象只有一个 你没有办法去new 因为构造器是被private修饰的 一般通过getInstance 的方法来获取它们的实例 getInstance 的返回值是一个对象的引用 并不是一个新
  • 数据库MySQL-索引(含常见面试题)

    目录 一 什么是索引 二 索引的作用 三 索引优缺点及场景 1 优点 2 缺点 3 使用场景 4 注意事项 四 索引的使用 1 索引分类 2 查看索引 3 创建索引 重点 4 索引和约束的区别 容易混淆 五 索引实现原理 索引失效 2 实现
  • Python——queue

    Queue Queue是python标准库中的线程安全的队列 FIFO 实现 提供了一个适用于多线程编程的先进先出的数据结构 即队列 用来在生产者和消费者线程之间的信息传递 基本FIFO队列 class Queue Queue maxsiz
  • C#设计模式——单例模式(Singleton Pattern)

    目录 一 概述 1 基础 二 实现 1 单线程 2 解决 多线程情况下 解决方案一 Sleep 解决方案二 加锁 三 扩展 一 概述 单例模式 gt 创建型设计模式 定义 保证一个类只有一个实例 并提供一个访问它的全局访问点 在第一个使用者