C# 中单例模式的可重用基类






4.71/5 (21投票s)
本文介绍了一个用于在 C# 中实现单例模式的可复用基类。
引言
让我们面对现实:每个人都喜欢单例!在过去的几年里,我在所有项目中都使用了许多这种小东西。最近,我厌倦了所有的“复制粘贴”,决定为我未来的所有项目编写一个可复用的单例基类。今天,我想与大家分享我的工作成果。
背景
这个基类的目的是将创建新单例的编码工作量减少到绝对最小值。这不仅意味着减少了您的编码工作量,而且还使您的单例更具可读性和可维护性。
这个基类也是线程安全的,这对于单例来说非常重要。
使用代码
我今天展示的代码由两个类组成
SingletonBase
是基类,每个单例都将从此类派生。SingletonExample
是一个非常简单的单例,它展示了如何从基类派生。
就是这样。全部内容。很简单,对吧?现在让我们直接开始!
示例单例
我将从本末倒置开始。下面的代码展示了一个非常简单的单例类,它派生自我们的神奇单例基类(稍后将展示)。
/// <summary>
/// This class is an example of how to create a singleton based on the singleton base class.
/// </summary>
class SingletonExample : SingletonBase<SingletonExample>
{
/// <summary>
/// Gets or sets a string property.
/// </summary>
public string SomeString {get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Singleton"/> class.
/// </summary>
private SingletonExample()
{
}
}
以下是您应该从上面的示例中学到的三个要点
- 单例从我们的基类
SingletonBase
派生。 - 我们将单例的类名作为类型参数传递 (
SingletonBase<SingletonExample>)
)。 - 我们定义了一个
private
构造函数。这很重要,因为我们不希望任何人实际实例化我们的单例。
如您所见,单例本身非常简单。所有繁重的工作都由基类完成。因此,您可以专注于实现您一直梦想的所有那些花哨的方法和属性。
单例基类
现在让我们进入好东西:我们的神奇单例基类! 在这里,我们要确保一次只存在我们的单例的一个实例。并且我们希望以线程安全的方式做到这一点。让我们一步一步地实现它
public abstract class SingletonBase<T> where T : class
{
...
在这里,我们声明我们的单例基类。如您所见,这是一个模板类(这就是“T”的来源)。这允许我们传递单例的类作为类型参数(参见上面的示例)。
接下来,我们必须回答一个重要的问题:用户如何访问单例的实例?这个问题通常通过提供一个名为“Instance
”的属性来解决(非常有创意,对吧?)。此属性返回我们的单例的唯一实例。以下是我们实现它的方法
/// <summary>
/// Static instance. Needs to use lambda expression
/// to construct an instance (since constructor is private).
/// </summary>
private static readonly Lazy<T> sInstance = new Lazy<T>(() => CreateInstanceOfT());
/// <summary>
/// Gets the instance of this singleton.
/// </summary>
public static T Instance { get { return sInstance.Value; } }
我认为上面的代码行值得稍微解释一下。首先,有一个名为“sInstance
”的静态成员。它保存了我们的单例的实例,并进行了延迟初始化。这可以通过使用 .NET 4.0 的 Lazy<T>
类轻松实现。并且最好的事情是:Lazy<T>
是完全线程安全的!
第二个要注意的事情是名为“CreateInstanceOfT
”的神秘方法。为什么我们需要它?为什么不简单地调用 new?好吧,这是因为我们的单例有一个私有构造函数(如果您忘记了,只需向上滚动一点到我们的 SingletonExample
类)。因此,我们不得不使用一个小技巧:Activator!
/// <summary>
/// Creates an instance of T via reflection since T's constructor is expected to be private.
/// </summary>
/// <returns></returns>
private static T CreateInstanceOfT()
{
return Activator.CreateInstance(typeof(T), true) as T;
}
借助这个小方法,我们现在可以实例化我们的单例类,尽管它的构造函数是私有的。巧妙,对吧?
整个单例基类
就是这样!现在您已经看到了整个单例基类。这是所有这些位组合在一起组成一个完整的类,准备好复制和粘贴
/// <summary>
/// A base class for the singleton design pattern.
/// </summary>
/// <typeparam name="T">Class type of the singleton</typeparam>
public abstract class SingletonBase<T> where T : class
{
#region Members
/// <summary>
/// Static instance. Needs to use lambda expression
/// to construct an instance (since constructor is private).
/// </summary>
private static readonly Lazy<T> sInstance = new Lazy<T>(() => CreateInstanceOfT());
#endregion
#region Properties
/// <summary>
/// Gets the instance of this singleton.
/// </summary>
public static T Instance { get { return sInstance.Value; } }
#endregion
#region Methods
/// <summary>
/// Creates an instance of T via reflection since T's constructor is expected to be private.
/// </summary>
/// <returns></returns>
private static T CreateInstanceOfT()
{
return Activator.CreateInstance(typeof(T), true) as T;
}
#endregion
}
兴趣点
您可能已经注意到 Lazy<T>
类仅在 .NET 4.0 及更高版本中可用。如果由于某种更高权力而被迫使用低于此版本的 .NET 版本,那么此代码不适合您。对不起!
历史
我根据下面的评论重写了方法 CreateInstanceOfT
。正如人们向我指出的那样,当涉及到实例化对象时,Activator 比反射更快。