
正文
单例模式以及在C#中的使用
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
下面做一些简要的说明。
1.
单例模式(Singleton Pattern),又称作单件模式,当然也有一种诙谐的称谓:单身模式。在经典的GoF所著的《Design Patterns》一书中,对单例模式有着详尽的介绍,这本书网上有全文版本。
2.
单例模式的意图是保证一个类仅有一个实例,并且要提供一个全局访问点来访问这个实例。通常这个全局访问点是一个静态方法或者C#中的一个属性。
3.
在C#中,典型的单例模式实现方法可以如下:
public
class
Manager
{
private
static
Manager Mgr;
//constructor must be private
private
Manager() { }
//public and static, or you can change it to Property instead of Method.
public
static
Manager GetInstance()
{
if
(Mgr ==
null
)
{
Mgr =
new
Manager();
}
return
Mgr;
}
}
|
当外部需要
Manager
的实例时,可以调用
GetInstance()
这个静态方法。由于
Manager
类的构造器是私有的,这也就避免了其他方式实例化这个
Manager
类。
GetInstance()
方法内部的实现,保证了全局中只有一个
Manager
实例。
4.
问题肯定不会这么简单就被解决,比如在多线程环境中,上述代码就会有很大的隐患。
-
有一种情况很常见:两个线程同时调用
GetInstance()方法; -
当某一个线程由于
Mgr为null而进入条件判断代码块的时候,而恰恰还没有执行实例化一个Manager对象,这时候另一个线程由于Mgr为null,所以也会进入这个条件语句中
上面两种情况,很显然都会创建
Manager
实例,这也就违背了单例模式的意图了。
利用C#的特性,我们可以把一个线程先锁住(
lock
),等到这个线程完成后,再让下一个线程访问
GetInstance()
方法:
private
static
readonly
object
syncObject =
new
object
();
//public and static, or you can change it to Property instead of Method.
public
static
Manager GetInstance()
{
//double check
if
(Mgr ==
null
)
{
lock
(syncObject)
{
if
(Mgr ==
null
)
{
Mgr =
new
Manager();
}
}
}
return
Mgr;
}
|
代码中用到了双重检查锁定(double check locking)的技术,是为了提高性能考虑,因为C#中
lock
语句是很耗性能的。第一道检查,是基于如果
Mgr
不为
null
的时候就不需要
lock
了,提高性能。第二道检查,是基于两个线程同时通过第一道检查后,第一个线程解锁后,由于
Mgr
此时已经不为
null
,所以第二个线程就不用实例化
Manager
了。
5.
单例模式有两种实现方式,主要基于构建的方式不同:
- 延迟初始化(Lazy Initialization),也叫“懒汉模式”:单例实例在第一次使用时被构建;
- 热初始化(Eager Initialization),也叫“恶汉模式”:单例实例在类加载时创建
前面创建单例模式的方式都属于延迟初始化。.NET 4.0以后提供了一个
Lazy<T>
泛型类,可以被应用于这个场景,省却代码的编写量。
public
class
Manager
{
private
static
Lazy<Manager> mgr =
new
Lazy<Manager>();
//version of Thread Safe
//private static Lazy<Manager> mgr = new Lazy<Manager>(true);
private
Manager() { }
public
static
Manager GetInstance()
{
return
mgr.Value;
}
}
|
Lazy<T>
的构造器重载版本可以帮我们解决多线程的问题。
C#使用静态初始化来完成单例模式中的热初始化。需要注意的是,不需要考虑多线程的问题,因为CLR会自动解决多线程同步的问题。如果程序经常要用到这个实例,运用热初始化可以显著提高性能。
public
class
Manager
{
private
static
readonly
Manager mgr =
new
Manager();
private
Manager() { }
public
static
Manager GetInstance()
{
return
mgr;
}
}
|
6.
StackOverflow中对于单例模式都是持否定态度的,比如这个:
In theory: when you need to restrict the instantiation of an object to one instance. In practice: never.
主要基于下面几个原因:
- 违反了单一职责原则
- 耦合度过大
- 单元测试基本无法进行
- 开发混淆,造成混乱。比如作为API提供的时候。
总之,
There‘s at most a can use but there no need.
文章来源:http://laobian.me/2013/02/singleton-pattern-with-csharp.html






