zoukankan      html  css  js  c++  java
  • .NET单例模式-------各种写法&&验证

    .NET单例模式-------各种写法&&验证

    前言

        单例模式对大家来说都不陌生,也很容易搞懂其原理,本篇文章也不提供单例模式的详细原理解析,本篇文章的目的是展示在C#中单例模式的各种实现方案(不完全,只是最通用的方式)以及其特点的验证(是不是真的线程安全,是不是真的延迟初始化?),写单例模式的文章都很多了,各种语言,但是很多地方都只说:本方式支持多线程、支持延迟初始化等,也有很多也提供为什么支持,下面我对所有大家通常使用的几种单例模式方案进行讲解和验证!有哪里不对的地方,希望能得到尊敬的读者们拍砖反馈,觉得好,顺带推荐一下,谢谢。

    简单原理解析

        单例模式,目标在于确保一个类仅仅能产生一个实例,并且提供一个全局访问点,获取该实例。

       无论哪一种单例模式变种,都离不开制作步骤这个中心。就好像无论哪家鸡爪店,其制作鸡爪方法都大同小异(都要先拿到鸡爪,洗鸡爪,弄熟鸡爪)。我们单例模式其实一样,其中心步骤包括:限制外部new出该对象的实例,内部提供该类型的一个唯一对象,提供一个全局访问点让外界获取到该唯一对象的实例进行操作。

       其实就这么简单,下面我要分析4个主要变种单例模式并且分别进行验证。

    准备工作

        我先提供一个大概框架给大家,方便用于测试,也可以不下载,继续看下去。   测试模板下载

        提供的模板很简单,只有一个类Person,下面给出要点:

        1.构造函数是私有的(避免new出新实例)。

        2.在构造函数里,我写的Console.WriteLine(主要是观察这个类的实例是何时初始化的,初始化了多少次)。

        3.静态方法getName()的作用是:在还没有通过全局访问点获取实例之前,调用这个getName方法,内部的实例会不会被初始化,如果不会,证明延迟初始化了,如果会,证明没有延迟初始化。

        下面先给出一个例子,这个例子是饿汉式单例模式。

    public class Person
        {
            /*饿汉式单例(线程安全,不支持延迟初始化)*/
     
            //初始化的时候会有反应,应用于延迟初始化的验证
            private Person() {
                Console.WriteLine("我初始化了");
            }
            private static String name = "Jarvin";
            //内部的唯一实例
            private static Person instance = new Person();
            //全局访问点,用于获取唯一实例
            public static Person getInstance()
            {
                return instance;
            }
     
            //实例方法
            public void Say()
            {
                Console.WriteLine("我是{0}",name);
            }
            /*静态的方法,应用于延迟初始化验证
             * 如果调用该方法之前还没初始,延迟初始化
             * 如果调用该方法之前初始化了,没有延迟初始化
             */
            public static String getName()
            {
                return name;
            }
            
        }

       测试的方式,要点:

        1.测试是否延迟初始化:测试方法是先调用Person.getName(),看结果返回name之前有没有被初始化来判断。

        2.测试线程安全:开3个多线程新任务,任务内容是获取唯一实例,并且调用实例方法。

      下面给出Main方法的代码:

    class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine(Person.getName());
                Console.WriteLine("下面进入多线程模式");
                for (int i = 0; i < 3; i++)
                {
                    Task.Factory.StartNew(letPersonSay);
                }
                Console.ReadKey();
            }
            private static void letPersonSay()
            {
                Person emperor = Person.getInstance();
                emperor.Say();
            }
        }

                注意:单例模式秀中中出现的以下代码只是测试需要使用,在正式的使用场景要去掉。

    int i=50000000;
    while (i > 0)
    { i--; }

    单例模式秀

       1.饿汉式单例

            初步判断:不支持延迟初始化,线程安全。

    private Person() {
                Console.WriteLine("我初始化了");
            }
            private static String name = "Jarvin";
            private static Person instance = new Person();
            public static Person getInstance()
            {
                int i = 50000000;
                while (i > 0)
                { i--; }
                return instance;
            }
    
            public void Say()
            {
                Console.WriteLine("我是{0}",name);
            }
            public static String getName()
            {
                return name;
            }

            验证:

            分析结果:在静态方法执行之前(Jarvin字符串)先初始化,不支持延迟初始化。然后进入多线程,没有问题。验证通过。

       2.懒汉式单例

            初步判断:支持延迟初始化,线程不安全。

    private Person() {
                Console.WriteLine("我初始化了");
            }
            private static String name = "Jarvin";
            private static Person instance;
            public static Person getInstance()
            {
                if (instance == null)
                {
                    int i=50000000;
                    while (i > 0)
                    { i--; }
                    instance = new Person();
                }
                return instance;
            }
    
            public void Say()
            {
                Console.WriteLine("我是{0}",name);
            }
            public static String getName()
            {
                return name;
            }

            验证:

            分析结果:在调用静态方法之前并没有先初始化,所以支持延迟初始化。进入多线程以后,有出现两次初始化,创建了两个Person类实例,线程不安全。验证通过。

       3.内部类式单例

            初步判断:支持延迟初始化,线程安全

    private Person()
            {
                Console.WriteLine("我初始化了");
            }
            public static Person getInstance()
            {
                return SingleHelper.GetEmperor();
            }
            private class SingleHelper
            {
                private static Person emperor = new Person();
                public static Person GetEmperor()
                {
                    int i = 50000000;
                    while (i > 0)
                    { i--; }
                    return emperor;
                }
            }
            private static string name = "Jarvin";
    
            public void Say()
            {
                Console.WriteLine("我是{0}", name);
            }
            public static String getName()
            {
                return name;
            }

            验证:

                分析结果:在调用静态方法之前并没有先初始化,所以支持延迟初始化。进入多线程以后,只初始化一次,线程安全。验证通过。

       4.双检查式单例

            初步判断:支持延迟初始化,线程安全

    private Person()
            {
                Console.WriteLine("我初始化了");
            }
            public static object Flag = new object();
            public static Person me;
            public static Person getInstance()
            {
                if (me == null)
                {
                    lock (Flag)
                    {
                        if (me == null)
                        {
                            int i = 50000000;
                            while (i > 0)
                            { i--; }
                            me = new Person();
                        }
                    }
                }
                return me;
            }
            private static string name = "Jarvin";
    
            public void Say()
            {
                Console.WriteLine("我是{0}", name);
            }
            public static String getName()
            {
                return name;
            }

            验证:

                分析结果在调用静态方法之前并没有先初始化,所以支持延迟初始化。进入多线程以后,只初始化一次,线程安全。验证通过。

     总结

        其实单例模式非常简单,聪明的读者们看到这里应该对大概通过的这四种单例,以及其特性都了解了。见笑啦,下面提供全部测试的源码,有兴趣的可以收藏。

                                       完整Demo下载

  • 相关阅读:
    奶酪(NOIP2017 Day2 T1)
    图的遍历(某谷P3916)
    20154331 EXP9web安全基础实践
    20154331 EXP8 web基础
    20154331EXP7 网络欺诈
    20154331 Exp6 信息搜集与漏洞扫描
    Exp5 MSF基础应用
    Exp4 恶意代码分析
    Exp3 免杀原理与实践
    20154331黄芮EXP2 后门原理与实践
  • 原文地址:https://www.cnblogs.com/Jarvin/p/3741057.html
Copyright © 2011-2022 走看看