随笔-137  评论-1674  文章-1  trackbacks-63
     本人在 横刀天笑 的一篇谈论单例模式的文章http://www.cnblogs.com/yuyijq/archive/2007/10/07/915941.html
里面有两种写单例的例子.有一种方法得到资深工程师的否定.事实证明他的想法是错误的。
 
   第一种:

    public class Singleton
 2{
 3    private static Singleton _instance = null;
 4    private static readonly object lockHelper = new object();
 5    private Singleton()
 6    {
 7    }

 8    public static Singleton CreateInstance()
 9    {
10        //这样lock以及lock块内的代码只会在第一次调用CreateInstance方法的时候执行,
11        //第一次调用该方法后_instance就不再为null了,if块内的代码就无须执行了
12        if(_instance == null)
13        {
14            lock(lockHelper)
15            {
16                if(_instance == null)
17                    _instance = new Singleton();
18            }

19        }

20        return _instance;
21    }

22}

      这种方法也是大家公认的能够在多线程下面正常工作的一种方法.

      原文中还提到了一种方法:

     实际上在很多地方我们可以采用另外一种初始化的方式,特别对于哪些实时系统或者哪些系统随时都会用的类(比如系统配置类),我们用另外一种实现方法就不需要考虑线程安全的问题了,它们的线程安全由.net运行时为我们作保证。

 

 1public class Singleton
 2{
 3    //先实例化出一个实例再说
 4    private static Singleton _instance = new Singleton();
 5        private Singleton(){}
 6    public static Singleton CreateInstance()
 7    {
 8        return _instance;
 9    }

10}

    博主说这两种方法都可以,我自己也不太清楚它们之间的区别,但是本人记忆力好,记下了.

    一次在中软面试中,有一个题让你写一个单例模式例子出来,我想都没想就把上面第二种方法写出来了,当然我忘了写私有构造函数.这个也不影响思路.但是他们的技术人员说这不是单例模式,因为每次初始化类的时候就会new一次.
private static Singleton _instance = new Singleton();他可能是说这个.

   之后我亲自测试了下博主说的第二种方法,说明是可行的,并非多线程.起码单线程是可以的.
   
   我想他的意思应该是这样的:
  
public class Singleton2
    
{
        
//先实例化出一个实例再说
        private static Singleton2 _instance = null ;
        
private DateTime _stime;
        
public DateTime sTime
        
{
            
get return this._stime; }
            
set this._stime = value; }

        }

        
private Singleton2()
        
{
            
this.sTime = DateTime.Now;

        }

        
public static Singleton CreateInstance()
        
{
            
if (_instance == null)
            
{
                
return new Singleton2();

            }

            
else
            
{
                
return _instance;
            
            }

            
        }

    }
     
     经过我的测试,第三种代码是不正确的.

     我在MSDN上查了关于static的解释:

     static
    修饰符指明成员属于类本身而不属于类的实例。即使创建了类的多个实例,给定应用程序中只存在 static 成员的一个副本。您只能通过对类的引用(而不是对实例的引用)来访问 static 成员。但是,在类成员声明中,可以通过 this 对象来访问 static 成员。
    类的成员可以使用 static 修饰符来标记。类、接口和接口的成员不能采用 static 修饰符。不能将 static 修饰符与任何继承修饰符(abstract 和 final)或版本安全修饰符(hide 和 override)组合。

     这就说明了博主的说法是正确的,无论初始化多少次类,但是只会存在静态成员的一个副本.
     据说那个面试官是特别资深的工程师,他应该也有他的理由说那不是单例,我想让大家给说说第二种代码是否是真正的单例模式. 

     谢谢大家对的帮助,今天回家看了下《HEAD FIRST 设计模式》在P181中明确的说明了上文中的第二种方法是可行的,也是真正的单例模式:

     原文是这样的:

     2。使用“急切”创建实例,页不用延迟实例化的做法
     如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切(eagerly)创建此单件,如下所示:
     代码和上文中第二种方法是一模一样的。还特意对:private static Singleton _instance = new Singleton();这条语句进行说明:
     在静态初始化器中创建单件,这段代码保证了线程安全。

     本文中第一种创建单件的方法属于延迟实例化的做法,只有当第一次调用的时候才会实例化类,没有用到时则不进行任何实例化操作。所有说当类实例化不是特别复杂,对服务器开销不大的时候这两种方法在最终作用上和效果上是一样的,没有本质区别。所以最后本人认为我面试中的面试官的观点是错误的。如果有理解错误的地方还望指点。
Tag标签: C#,.NET
posted on 2008-05-09 17:39 姜敏 阅读(1760) 评论(23)  编辑 收藏 所属分类: 设计模式

评论:
#1楼 2008-05-09 17:43 | 玉开      
第二种方法也是独身模式,但是在程序集载入时就生成了一个Singleton的实例;而第一种方法则是在第一次使用时才生成实例。
  回复  引用  查看    
#2楼 2008-05-09 17:49 | Artech      
我每次使用的都是第二种方式 private static Singleton _instance = new Singleton(); =〉private readonly static Singleton _instance = new Singleton();就更好了。
  回复  引用  查看    
#3楼 2008-05-09 17:51 | lovecherry      
呵呵,的确很资深
  回复  引用  查看    
#4楼 2008-05-09 17:52 | 怪怪      
是单例, 但是由于你没有写静态构造函数,初始化时间不确定,容易造成问题。 你在google或者baidu上查一下

beforefieldinit

就知道了。 一般资深工程师还是比较靠谱的, 特别资深的就不好说了。

  回复  引用  查看    
#5楼[楼主] 2008-05-09 17:54 | 姜敏      
@玉开
就是说,当应用程序一运行,无论是否调用Singleton,它都会被初始化吗?
那这样的话,应该还是第一种方法好了.

  回复  引用  查看    
#6楼[楼主] 2008-05-09 17:57 | 姜敏      
@怪怪
这么说,你也同意 横刀天笑 的第二种写单例的方法了,看来是可以构成单例.那这么说我的理解以及原文博主的理解是正确的.

  回复  引用  查看    
#7楼 2008-05-09 18:07 | 怪怪      
@姜敏
是地, 只是注意, 当没有静态构造函数的时候, 在你没有访问这个field之前的某一个不固定的时间, 会初始化。 而有了静态构造函数, 在你第一次访问这个类的任何东西之前那一刻初始化。

  回复  引用  查看    
#8楼 2008-05-09 19:17 | alonesword[未注册用户]
没有申明 private 的构造函数引起。
  回复  引用    
#9楼 2008-05-09 20:07 | floodpeak      
lz可以参考一下《Java与模式》第15章,你这里的第一种方式被作者称为懒汉式单例,第二种方式称为饿汉式单例
  回复  引用  查看    
#10楼 2008-05-09 20:08 | floodpeak      
@怪怪
静态构造函数?

  回复  引用  查看    
#11楼 2008-05-09 20:41 | Allen Lee      
--引用--------------------------------------------------
玉开: 第二种方法也是独身模式,但是在程序集载入时就生成了一个Singleton的实例;而第一种方法则是在第一次使用时才生成实例。
--------------------------------------------------------

“独身模式”,妙!

  回复  引用  查看    
#12楼 2008-05-09 20:53 | 青羽      
@floodpeak
类型构造器
internal sealed class SomeType(){
private static int i=1;
}
等同于
internal sealed class SomeType(){
private static int i;
static SomeType(){
i=1;
}
}
类型构造器永远没有参数
类型构造器永远都是私有的

  回复  引用  查看    
#13楼 2008-05-09 21:51 | 横刀天笑      
呵呵,那我不是比那个特别资深工程师还那个一点?可我只是个普通程序员啊555555555 开个玩笑,

嗯,写那篇博客的时候查的资料不是很多
实际上综合Artech和怪怪的写法才最好了

public class Singleton
{
prviate readonly static _instance = new Singleton();
static Singleton()
{}
prviate Singleton(){}
public static Singleton CreateInstance()
{

return _instance;
}
}

  回复  引用  查看    
#14楼 2008-05-09 22:38 | Angel Lucifer      
第二种方法严格意义上来说,不是良好的Singleton模式实现,它是非线程安全的。当在多线程环境下,它可能会构造出多个实例。

第一种使用双检锁技巧的Singleton,是延迟初始化的标准线程安全用法。

不过,如果是无参数的构造实例,则推荐13楼的用法。
有参数的推荐楼主的第一种用法。

  回复  引用  查看    
#15楼 |

  
#16楼 2008-05-10 00:08 | 何随风      
帝up ,学习中....
  回复  引用  查看    
#17楼[楼主] 2008-05-10 01:08 | 姜敏      
@Angel Lucifer
第二种方法严格意义上来说,不是良好的Singleton模式实现,它是非线程安全的。当在多线程环境下,它可能会构造出多个实例。

这样说不对吧,Artech和怪怪还有几位朋友都同意啊,我觉的是线程安全的,因为这是由.net机制自己完成的,静态变量.不过我想用的比较多的应该还是最好的.

  回复  引用  查看    
#18楼 2008-05-10 13:04 | Angel Lucifer      
--引用--------------------------------------------------
姜敏: @Angel Lucifer
第二种方法严格意义上来说,不是良好的Singleton模式实现,它是非线程安全的。当在多线程环境下,它可能会构造出多个实例。

这样说不对吧,Artech和怪怪还有几位朋友都同意啊,我觉的是线程安全的,因为这是由.net机制自己完成的,静态变量.不过我想用的比较多的应该还是最好的.
--------------------------------------------------------

它是线程安全的,呵呵。偶记错了,抱歉。
不过我还是建议使用static readonly来修饰,而不是仅仅使用static来修饰。

  回复  引用  查看    
#19楼 |

  
#20楼 2008-05-10 23:07 | oo縼箻ㄗs.鋒      
可以去看看terryLee写的单例设计的文章.
  回复  引用  查看    
#21楼 2008-05-11 04:24 | 久永[未注册用户]
一次在中软面试中,有一个题让你写一个单例模式例子出来,我想都没想就把上面第二种方法写出来了,>>>>当然我忘了写私有构造函数.<<<<<
——关键问题就在这里,你丢掉私有化函数的话,问题与过程就完全变了。那个技术人员否定的大概并不是第二种方法,而是你写的代码。(实际上,第二种才是我平常用的方法,而且个人觉得比第一种简洁可靠)

  回复  引用    
#22楼 2008-05-12 01:35 | 镜涛      
如果程序中不一定会用到这个实例,哪么就在用它的时候才初始化(延迟初始化);如果牵扯到同步问题哪么就加锁,利用静态构造函数保证用时立即初始化。如果一定会需要这个实例就立即初始化
  回复  引用  查看    
#23楼 2009-03-27 22:49 | jeky      
static readonly Singleton instance = new Singleton(); // 静态只读实例

static Singleton() { } // 静态构造函数,用于是初始化 instance 变量

Singleton() { } // 实例构造函数

  回复  引用  查看    



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1190328


相关文章:

相关链接: