技术开发 频道

c#设计模式实例


    关于设计模式的书籍和资料已经有很多了, 本文不重复的去探讨设计模式的理论知识,主要探讨在C#中使用设计模式的方案选择和实现细节,适合设计模式初学者阅读。对于对设计模式有一定了解的开发人员建议阅读《C#设计模式》一书。本文下面将讲述单件模式的应用。

简介

    单件模式是设计模式中最简单也是最常用的模式之一。在开发的过程中我们也经常需要用到单件模式。在C语言时代,我们通过全局变量来实现对唯一数据的访问,但是这种方式并不能保证数据的唯一性。之后人们在面向对象编程中使用设计模式,并提出了单件这种概念。那么在.net框架下面是如何来实现单件模式的呢?通常的情况下是使用静态控制符(static)来实现的。下面我将用三个例子来说明单件模式,并且说明这三个例子各自的优点和缺点。

经典的单件模式
/// <summary> 
/// 使用sealed,类不能被继承
/// </summary>
public sealed class classSingleton
{
static private classSingleton _instance = null;

/// <summary>
/// 阻止被new操作符实例化
/// </summary>
private classSingleton()
{
}

/// <summary>
/// 获取实例接口
/// </summary>
static public classSingleton Instance
{
get
{
return (null == _instance) ? new classSingleton() : _instance;
}
}
}
   上面这个例子是比较经典的单件模式例子,通过类自身的访问控制来实现应用程序全局只有一个实例对象。这种方式比较简单,并且足以满足在单线程模式下的单件模式现实,然而这种方式在多线程情景下会是什么样子呢?存在被多次实例话的可能,由于在Instance接口中没有进行访问保护,当两个线程同时请求Instance时,接口就会返回两个classSingleton实例。



一般的.net框架下的单件模式
public sealed class DotnetSingleton 
{
/// <summary>
/// 加入readonly , 使得_instance只能在声明时或构造函数中被初始化
/// </summary>
static readonly DotnetSingleton _instance = new DotnetSingleton();

// 告诉C#编译器不要对该类进行类别标记, 并可用于初始化_instance
static DotnetSingleton()
{
}

private DotnetSingleton()
{
}

static public DotnetSingleton Instance
{
get { return _instance; }
}
}
   注:
   MSDN上关于readonly和const的区别:readonly 关键字与 const 关键字不同。const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。另外,const 字段为编译时常数,而 readonly 字段可用于运行时常数。

   在.net框架下,我们通常使用static标识符来实现单件模式,由static定义的对象或函数只能通过class.*方式来引用那么很显然在一个应用程序中域中是不会出现多个类实例,并且访问区域是全局的。使用static标志使得类实例对象只在第一次调用时被分配一次,从而保证了线程访问的安全性并且它是依赖于公共语言库来实现对象初始化的,也省去了对象释放的过程。因此这也是在.net框架下实现单件模式的普遍选择。然而,该方案有两个缺点:第一,由于初始化是由框架完成的这使得你对对象初始化之前的工作失去了控制能力;第二,如果该类存在其他的静态数据成员,那么在这些数据成员被引用时_instance也会被初始化掉,这显然不是你想要的结果。


改进后的.net框架下的单件模式

public sealed class ExDotnetSingleton 
{
/// <summary>
/// 使用这个类来进行迟延初始化单件对象,
/// 并限制了的访问权限
/// </summary>
internal class internalSingleton
{
static internalSingleton()
{
}

/// <summary>
/// 限制了变量的访问权限在ExDotnetSingleton区域
/// </summary>
internal static readonly ExDotnetSingleton _instance = new ExDotnetSingleton();
}

private ExDotnetSingleton()
{
}

static public ExDotnetSingleton Instance
{
get { return internalSingleton._instance; }
}

/// <summary>
/// 定义一些函数来完成单件被初始化前的工作
/// </summary>
/// <param name="args"></param>
static public void InitSomeSetting(object [] args)
{

}
}
   上面的这个例子改进了上一个例子初始化控制问题,真正的延迟了_instance初始化到运行期。不仅仅如此,它还有其他优点:
1. 延迟初始化时间,并允许在初始化前做其他工作
2. 多线程中是安全的
3. 利用内部类分离了实现接口,并运行通过继承来实现代码重用
4. 同样利用了 .net框架的公共语言层来完成初始化工作

   但是存在效率上的缺点增大了类的体积。

相关的议题

    对于以上的议题,有人会提出这样的疑问为什么不将整个类声明为static?首先这个将整个类声明为static的确是在.net中常用的方式,例如: Console类就是一个static类。但是有一个问题就是如果整个类是static的话那么我们就无法对类进行初始化工作。
总结

    以上三个例子通过代码的方式,逐步的体现了如何利用.net框架提供的功能,更好的去实现一种模式。并且也告诉了我们何时去使用internal,readonly和static这三个关键字。
0
相关文章