具有可触发初始化的C#Singleton模式

我需要一个单身人士:

  • 很懒
  • 是线程安全的
  • 在施工时加载一些值
  • 可以随时查询这些值
  • 初始化可能在查询开始之前的某个精确时间发生 – 所以我必须能够以某种方式从外部触发它。 当然,多次触发应该只进行一次初始化。

我使用.NET 3.5。

我使用静态子类开始使用Jon Skeet的实现 (第5版):

public sealed class Singleton { IEnumerable Values {get; private set;} private Singleton() { Values = new[]{"quick", "brown", "fox"}; } public static Singleton Instance { get { return Nested.instance; } } private class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } } 

除了“从外部触发初始化”之外,几乎所有方框都会打勾。 由于实际初始化发生在ctor内部,因此不会发生多次。

如何实现这一目标?

单例将使用如下:

 public static void Main(){ //do stuff, singleton should not yet be initialized. //the time comes to initialize the singleton, eg a database connection is available //this may be called 0 or more times, possibly on different threads Singleton.Initialize(); Singleton.Initialize(); Singleton.Initialize(); //actual call to get retrieved values, should work var retrieveVals = Singleton.Instance.Values; } 

好像你可以这样做:

 public sealed class Singleton { IEnumerable Values {get; private set;} private Singleton(bool loadDefaults) { if (loadDefaults) Values = new[]{"quick", "brown", "fox"}; else Values = new[]{"another", "set", "of", "values"}; } public static Singleton Instance { get { return Nested.instance; } } public static void Initialize() { Nested.Initialize(); } private class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(true); private static object instanceLock = new object(); private static bool isInitialized = false; public static void Initialize() { lock(instanceLock) { if (!isInitialized) { isInitialized = true; instance = new Singleton(false); } } } } } 

或者创建一个将要更新的单个实例:

 public sealed class Singleton { IEnumerable Values {get; private set;} private Singleton() { Values = new[]{"quick", "brown", "fox"}; } public static Singleton Instance { get { return Nested.instance; } } private static object instanceLock = new object(); private static bool isInitialized = false; public static void Initialize() { lock(instanceLock) { if (!isInitialized) { isInitialized = true; Instance.Values = new[]{"another", "set", "of", "values"}; } } } private class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } } 

第三个变体基于您的不可变注释和删除嵌套类注释:

 public sealed class Singleton { IEnumerable Values {get; private set;} private Singleton() { Values = new[]{"quick", "brown", "fox"}; } private static Singleton instance; private static object instanceLock = new object(); public static Singleton Instance { get { Initialize(); return instance; } } public static void Initialize() { if (instance == null) { lock(instanceLock) { if (instance == null) instance = new Singleton(); } } } } 

您可以设置一个可以从外部触发的Initialize方法,如果您需要稍后进行初始化,但如果每次触发时值都不同,则它不能是静态的,这违反了Singleton模式。

根据你的例子,它没有变量,我假设你只是在初始化发生时延迟(例程而不是构造函数),但你的问题表明你想要不同的值,但如果多个初始化发生在一起,它只会初始化一次,所以我对此感到有点困惑。

我不确定你是否只需要Singleton implmentation,但是如果没有关于Initialize()每次是否运行相同的代码或者是否具有某种类型的变量性质的信息,则无法完全回答。

您可以使用双重检查锁定模式。 只需在您的Singleton类中添加以下代码:

 public sealed class Singleton { .......................... private static object locker = new object(); private static bool initialized = false; public static void Initialize() { if (!initialized){ lock(locker) { if (!initialized){ //write initialization logic here initialized = true; } } } } ....................... } 

你可以做这样的事情

 public sealed class Singleton { IEnumerable Values { get; set; } private Singleton() { Console.WriteLine("-- Private Singleton constructor"); Values = new[] { "quick", "brown", "fox" }; } public static Singleton Instance { get { Console.WriteLine("- Singleton Instance"); return Nested.instance; } } public static void Initialize() { Console.WriteLine("- Singleton Initialize"); Nested.Initialize(); } internal class Nested { private static object syncRoot = new object(); // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { Console.WriteLine("-- Static Nested constructor"); } internal static readonly Singleton instance = new Singleton(); internal static void Initialize() { lock (syncRoot) { Console.WriteLine("-- Locked"); Console.WriteLine("--- Nested Initialize"); Console.WriteLine("-- Unlocked"); } } } } 

用法

 class Program { static void Main(string[] args) { var i = Singleton.Instance; i = Singleton.Instance; Console.WriteLine("-----"); Singleton.Initialize(); Singleton.Initialize(); Singleton.Initialize(); Console.Read(); } } 

哪个输出

 - Singleton Instance -- Private Singleton constructor -- Static Nested constructor - Singleton Instance ----- - Singleton Initialize -- Locked --- Nested Initialize -- Unlocked - Singleton Initialize -- Locked --- Nested Initialize -- Unlocked - Singleton Initialize -- Locked --- Nested Initialize -- Unlocked 
 public class Singleton where T : class, new() { private static T instance; public static T Instance { get { if (instance == null) { throw new Exception("singleton needs to be initialised before use"); } return instance; } } public static void Initialise(Action initialisationAction) { lock(typeof(Singleton)) { if (instance != null) { return; } instance = new T(); initialisationAction(instance); } } } 

我的第一个想法就是使用分配给单例实例的一次性变量,它会(可能?)触发初始化

 static Main() { var unused = Singleton.Instance; //this should initialize the singleton, unless the compiler optimizes it out. //I wonder if the compiler is smart enough to see this call has side effects. var vals = Singleton.Instance.Values; } 

…但是我试图避免使用副作用进行编程,所以让我们的意图更加清晰。

 public class Singleton { public static void Initialize() { //this accesses the static field of the inner class which triggers the private Singleton() ctor. Instance._Initialize(); } private void _Initialize() { //do nothing } [the rest as before] } 

所以用法是:

 static Main() { //still wondering if the compiler might optimize this call out Singleton.Initialize(); var vals = Singleton.Instance.Values; } 

顺便说一下这也有用:

 static Main() { var vals = Singleton.Instance.Values; } 

抛开编译器优化,我认为这涉及所有要求。