C# Singleton pattern with triggerable initialization

I need a singleton that:

  • is lazy loaded
  • is thread safe
  • loads some values at construction
  • those values can be queried at any time
  • the initialization MAY happen at some precise time, before the querying begins - so I must be able to trigger it from the outside somehow. Of course, triggering multiple times should only do the initialization once.
  • I use .NET 3.5.

    I've started with Jon Skeet's implementation (5th version) using a static subclass:

    public sealed class Singleton
    {
        IEnumerable<string> 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();
        }
    } 
    

    This ticks almost all the boxes, except the "trigger initialization from outside". Since the actual initialization happens inside the ctor, it can't happen more than once.

    How can this be accomplished?

    The singleton will be used like this:

    public static void Main(){
    
        //do stuff, singleton should not yet be initialized.
    
        //the time comes to initialize the singleton, e.g. 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;
    
    }
    

    Seems like you could do:

    public sealed class Singleton
    {
        IEnumerable<string> 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);
                    }
                }
            }
    
        }
    } 
    

    Or to create a single instance that will be updated:

    public sealed class Singleton
    {
        IEnumerable<string> 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();
        }
    } 
    

    And the third variation based on your immutable comment and removal of Nested class comment:

    public sealed class Singleton
    {
        IEnumerable<string> 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();
                }
            }
        }
    } 
    

    You can set up an Initialize method that can be fired from outside, if you need the initialize to happen later, but if the values are different on each time it is fired, then it cannot be static, which violates the Singleton pattern.

    Based on your example, which has no variables, I assume you are just delaying when the initialization happens (routine rather than constructor), but your question suggests you want different values, but if multiple initializations happen close together, it only initializes once, so I am a bit confused on this.

    I am not sure you need a Singleton only implmentation, but cannot fully answer without information on whether or not the Initialize() runs the same code every time or has some type of variable nature.


    You can use double-checked locking pattern. Just add following code in you Singleton class:

    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;
                     }
                  }
                }
            }
    
    .......................
    
    }
    
    链接地址: http://www.djcxy.com/p/8736.html

    上一篇: Android发布到Facebook墙壁,stream.publish自几天以来破裂

    下一篇: 具有可触发初始化的C#单例模式