• 【面试题2】实现Singleton模式


    【题目描述】

    设计一个类,我们只能生成该类的一个实例。

    【解决方案】

    1. 只适用于单线程环境

    单线程环境下可以正常工作。

    但是在多线程环境下,如果两个线程同时判断到instance为null,那么这两个线程都会创建一个实例,此时的Singleton就不满足单例模式的要求了。

     1     public sealed class Singleton
     2     {
     3         private Singleton()
     4         { 
     5         }
     6 
     7         private static Singleton instance = null;
     8         public static Singleton Instance
     9         {
    10             get
    11             {
    12                 if (instance == null)
    13                     instance = new Singleton();
    14                 return instance;
    15             }            
    16         }
    17     }

    2. 多线程环境能工作,但效率不高

    加同步锁处理后,能保证在多线程环境中也只创建一个实例。

    但是由于在调用属性Instance时都会加同步锁,加锁又是一个非常耗时的操作,在没有必要的时候我们应该尽量避免。

     1     public sealed class Singleton
     2     {
     3         private Singleton()
     4         {
     5         }
     6 
     7         private static readonly Object syncObj = new object();
     8 
     9         private static Singleton instance = null;
    10         public static Singleton Instance
    11         {
    12             get
    13             {
    14                 lock (syncObj)
    15                 {
    16                     if (instance == null)
    17                         instance = new Singleton();
    18                 }
    19                 return instance;
    20             }
    21         }
    22     }

     3. 加同步锁前后两次判断实例是否已经存在

    加锁之前进行判断实例是否已经创建,可以保证实例已经创建后,则可以直接返回instance,而无需去进行加锁操作,保证只创建一个实例,相比上述方法,也提高了效率。

    由于此代码实现比较繁杂且容易出错,我们可以尝试更好的解决方案。

     1     public sealed class Singleton
     2     {
     3         private Singleton()
     4         {
     5         }
     6 
     7         private static readonly Object syncObj = new object();
     8 
     9         private static Singleton instance = null;
    10         public static Singleton Instance
    11         {
    12             get
    13             {
    14                 if (instance == null)
    15                 {
    16                     lock (syncObj)
    17                     {
    18                         if (instance == null)
    19                             instance = new Singleton();
    20                     }
    21                 }
    22                 return instance;
    23             }
    24         }
    25     }

    4. 利用静态构造函数

    由于C#是在调用静态构造函数时初始化静态变量,.NET运行时能够确保只调用一次静态构造函数,这样我们就可以确保只初始化一次instance。

    但是,根据静态构造函数的特性,实例instance并不是第一次调用属性Singleton.Instance时创建,而是在第一次用到Singleton的时候就会被创建。假设我们在Singleton中添加一个静态方法调用该静态方法是不需要创建实例的,但是由于你使用了Singleton,他会自动调用静态构造函数并创建实例,造成过早地创建实例,从而降低内存的使用效率。

     1     public sealed class Singleton
     2     {
     3         private Singleton()
     4         {
     5         }
     6 
     7         private static Singleton instance = new Singleton();
     8         public static Singleton Instance
     9         {
    10             get
    11             {
    12                 return instance;
    13             }
    14         }
    15     }

    5. 实现按需创建实例

    内部定义一个私有类型Nested,类型Nested只在调用Singeton.Instance时被使用,由于其私有属性他人无法使用Nested类型。

    因此当我们第一次调用Sington.Instance时,会调用Nested的静态构造函数,并初始化静态变量。如果我们不调用属性Sington.Instance,则不会创建instance实例,这样就做到了按需创建。

     1     public sealed class Singleton
     2     {
     3         Singleton()
     4         {
     5         }
     6 
     7         public static Singleton Instance
     8         {
     9             get
    10             {
    11                 return Nested.instance;
    12             }
    13         }
    14 
    15         class Nested
    16         {
    17             static Nested()
    18             {
    19             }
    20             internal static readonly Singleton instance = new Singleton();
    21         }
    22     }

    【本题扩展】

    五种单例模式的实现把类型标记为sealed,表示他们不能作为其他类型的基类。现在我们要定义一个表示总统的类型President,可以从该类型继承出FrenchPresident和AmericanPresident等类型,这些派生类都只能产生一个实例。请问该如何设计实现这些类型?

    我的代码实现,仅供参考:

     1     class President
     2     {
     3     }
     4 
     5     sealed class FrenchPresident:President
     6     {
     7         FrenchPresident()
     8         {
     9         }
    10 
    11         public static FrenchPresident Instance
    12         {
    13             get
    14             {
    15                 return Nested.instance;
    16             }
    17         }
    18 
    19         class Nested
    20         {
    21             static Nested()
    22             {
    23             }
    24             internal static readonly FrenchPresident instance = new FrenchPresident();
    25         }
    26     }
    27 
    28     sealed class AmericanPresident : President
    29     {
    30         AmericanPresident()
    31         {
    32         }
    33 
    34         public static AmericanPresident Instance
    35         {
    36             get
    37             {
    38                 return Nested.instance;
    39             }
    40         }
    41 
    42         class Nested
    43         {
    44             static Nested()
    45             {
    46             }
    47             internal static readonly AmericanPresident instance = new AmericanPresident();
    48         }
    49     }
  • 相关阅读:
    加入收藏
    c#在窗口标题栏上加按钮转载自:http://tech.ddvip.com/200810/122483002782273.html
    关于获取c# 的winform中DataGird控件选中行的值
    GridView中使用LinkButton添加启用禁用功能
    winfrom定制窗体样式
    C#读取xml文件
    C#winform程序如何与js交互
    ASP.NET不允许输入空格
    Winform TextBox中只能输入数字的几种常用方法(C#)
    winfrom中的webbrowser与web里面的html以及js的交互
  • 原文地址:https://www.cnblogs.com/HuoAA/p/4794922.html
Copyright © 2020-2023  润新知