Skip to main content

Different example techniques of implementing Singleton patterns in C#.

using System;
using System.Threading;

namespace MsPressClr.Console
{
    static class Singletons
    {
        public static class V1
        {
            public sealed class Singleton
            {
                // _lock is required for thread safety and having this object
                // assumes that creating the singleton object is more expensive
                // than creating a System.Object object and that creating the
                // singleton object may not be necessary at all. Otherwise, it
                // is more efficient and easier to just create the singleton
                // object in a class constructor
                private static readonly Object _lock = new Object();

                // This field will refer to the one Singleton object
                private static Singleton _instance;

                // Private constructor prevents any code outside this class
                // from creating an instance
                private Singleton()
                {
                    /* ... */
                }

                // Public, static method that returns the Singleton object
                // (creating it if necessary)
                public static Singleton GetInstance()
                {
                    // If the Singleton was already created, just return
                    // it (this is fast)
                    if (_instance != null)
                    {
                        return _instance;
                    }

                    Monitor.Enter(_lock); // Not created, let 1 thread create it
                    if (_instance == null)
                    {
                        // Still not created, create it
                        Singleton localInstance = new Singleton();

                        // Save the reference in _instance. Ensures that the
                        // reference in 'localInstance' can be published into
                        // '_instance', only after the constructor has finished
                        // executing.
                        Volatile.Write(ref _instance, localInstance);
                    }
                    Monitor.Exit(_lock);

                    // Return a reference to the one Singleton object
                    return _instance;
                }
            }
        }

        public static class V2
        {
            public sealed class Singleton
            {
                private static readonly Singleton _instance = new Singleton();

                // Private constructor prevents any code outside this class
                // from creating an instance
                private Singleton() { }

                // Public, static method that returns the Singleton object
                // (creating it if necessary)
                public static Singleton GetInstance()
                {
                    return _instance;
                }
            }
        }

        public static class V3
        {
            public sealed class Singleton
            {
                private static Singleton _instance;

                // Private constructor prevents any code outside this class from
                // creating an instance
                private Singleton() { }

                // Public, static method that returns the Singleton object
                // (creating it if necessary)
                public static Singleton GetInstance()
                {
                    if (_instance != null)
                    {
                        return _instance;
                    }

                    // Create a new Singleton and root it if another thread
                    // didn't do it first
                    Singleton localInstance = new Singleton();
                    Interlocked.CompareExchange(ref _instance, localInstance, null);

                    // If this thread lost, then the second Singleton
                    // object gets GC'd

                    return _instance; // Return reference to the single object
                }
            }
        }

        // The FCL offers two types that encapsulate the patterns described in
        // this section. The generic System.Lazy class looks like this
        // (some methods are not shown).
        public static void Go()
        {
            //
            // public enum LazyThreadSafetyMode
            // {
            //     None,                    // No thread­safety support at all (good for GUI apps)
            //     ExecutionAndPublication, // Uses the double­check locking technique
            //     PublicationOnly,         // Uses the Interlocked.CompareExchange technique
            // }
            //

            //
            // Create a lazy ­initialization wrapper around getting the DateTime
            Lazy<String> s = new Lazy<String>(() => DateTime.Now.ToString(), true);

            System.Console.WriteLine(s.IsValueCreated); // Returns false because Value not queried yet
            System.Console.WriteLine(s.Value);          // The delegate is invoked now
            System.Console.WriteLine(s.IsValueCreated); // Returns true because Value was queried
            Thread.Sleep(10000);                        // Wait 10 seconds and display the time again
            System.Console.WriteLine(s.Value);          // The delegate is NOT invoked now; same result

            //
            // Also, being able to explicitly specify a synchronization object
            // to the "EnsureInitialized" method's syncLock parameter allows
            // multiple initialization functions and fields to be protected by
            // the same lock.
            String name = null;
            LazyInitializer.EnsureInitialized(ref name, () => "Jeff");
            System.Console.WriteLine(name); // Jeff

            LazyInitializer.EnsureInitialized(ref name, () => "Richter");
            System.Console.WriteLine(name); // Jeff
        }
    }
}