Best Singleton in C# [6 Approaches To Interview Question]

Have you been asked to implement a singleton in C# in your last software developer interview? You may find this question a simple interview question – This is a very simple pattern to implement. However, this may be a tricky question if you ignore the details. Did you answer what the interviewer wanted to hear? How does the interviewer evaluate your solution?

Understanding the Singleton Pattern

The first thing the candidate needs to know is what is the singleton pattern. If the interviewer was not kind enough to explain what is the singleton pattern – you could find about on Wikipedia

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object.

Singleton pattern on Wikipedia

Let’s see what the requirements from the singleton class are:

  • The class should provide a mechanism to create one and only one object.
  • The class should provide a mechanism to access this single object.
Singleton in C#
Singleton in C#

How would you implement those requirements?

Solution #1: Singleton in C# – The Naive Solution

You can quickly fulfill the first requirement – Add a mechanism to create one and only one object. First, decide that the singleton class is responsible for creating its single instance. Second, restrict the instantiation of the class by entities of other classes (in their constructors, methods, field’s initializers, property’s getters, and setters and their static counterparts).

Luckily, C# language allows us to restrict access to constructors, methods, properties, and fields. For example, by declaring the singleton constructor as private, the only entities that can create this class are its members. The following code snippet demonstrates this restriction.

C# Supports Restricted Access
 class Singleton {
     // When we declare the class constructor as private, no other class can instantiate the class.
     private Singleton() {
     }
     public static void Main(string[] args) {
     }
 }

 class OtherClass {
     // when uncommenting the Singleton class instantiation in the following entities ,
     // the compiler will complain that Singleton constructor is inaccessible in the
     // current context.
     public OtherClass()        { /* Singleton singleton = new Singleton(); */ }
     static OtherClass()        { /* Singleton singleton = new Singleton(); */ }
     public void Method()       { /* Singleton singleton = new Singleton(); */ }
     static void MethodS()      { /* Singleton singleton = new Singleton(); */ }
     public int Property  { get { /* Singleton singleton = new Singleton(); */ return 1; }
                            set { /* Singleton singleton = new Singleton(); */           } }
     static int PropertyS { get { /* Singleton singleton = new Singleton(); */ return 1; }
                            set { /* Singleton singleton = new Singleton(); */           } }
     /* public Singleton _field  = new Singleton(); */
     /* static Singleton _fieldS = new Singleton(); */
 }

We also need the class to create one and only one instance. The obvious solution is to declare a public readonly static field and initialize it with a new class instance.

It will ensure that the application creates only one object since the static fields initializers are executed in a given application domain at most once. The other classes will access this object via this static field since it is public but cannot change its contents since it is readonly.

Singleton #1: Singleton in C# – The Naive Solution
 class Singleton {
     private Singleton() {
         System.Console.WriteLine("Singleton");
     }
     public void DoWork() {
         System.Console.WriteLine("DoWork");
     }

     public static readonly Singleton Instance = new Singleton();
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }

What Do You Think?

First, we can say that this solution is correct. However, Can you provide a better solution? The object is created before it is needed. It may impact the performance of the program if the instantiation consumes precious execution time and memory resources. Can you find another solution – a solution that will create the object only when needed?

Solution #2: Singleton in C# – Lazy Without thread safety

You may think about the following lazy solution: In this solution, we create the object only when needed. To do this, we can use static property. The class allows other classes to access its single object via static property. We will create the object in the static property if it does not exist yet and return the single object.

Solution #2: Singleton in C# – Lazy Without Thread Safety
 class Singleton {
     private Singleton() {
         System.Console.WriteLine("Singleton");
     }
     public void DoWork() {
         System.Console.WriteLine("DoWork");
     }

     public static Singleton Instance {
         get {
             if (_instance == null) {         // <== A
                 _instance = new Singleton(); // <== B
             }
             return _instance;
         }
     }
     private static Singleton _instance = null;
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }
When We Run The Program, We Get The Output
 Main
 Singleton
 DoWork

What Do You Think?

This solution seems to work, but there are situations the answer is not correct anymore. For example, can you think about situation where there is no guarantee that the application creates only one object?

In multi-threaded programs, there can be situations where the application creates several objects. Suppose one thread wants to access the single object via the static property when the object does not exist yet. First, the thread executes the if statement (A) and finds out the object does not exist yet. Next, it starts to create the object (B) but then gets suspended. Now another thread tries to access the single object via the static property. The second thread executes the if statement (A) and finds out the object does not exist yet (since the first thread did not finish creating the object). Eventually, the 2 threads will create an object.

The following program demonstrates this problem:

Solution #2 Is Not Correct In multi-threaded Programs
 using System.Threading;

 class Singleton {
     private Singleton() {
         System.Console.WriteLine("Singleton : Start");
         if (_first ) {
             _first = false;
             Thread.Sleep(5000);
         }
         _count++;
         System.Console.WriteLine("Singleton End. There are {0} objects", _count);
     }
     public void DoWork(int xx) {
         System.Console.WriteLine("DoWork {0}", xx);
     }

     public static Singleton Instance {
         get {
             if (_instance == null) {
                 _instance = new Singleton();
             }
             return _instance;
         }
     }
     private static Singleton _instance = null;
     private static bool _first = true;
     private static int _count = 0;
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main Started");
         Thread s1 = new Thread(() => Singleton.Instance.DoWork(1)); s1.Start();
         Thread s2 = new Thread(() => Singleton.Instance.DoWork(2)); s2.Start();
         s1.Join(); s2.Join();
         System.Console.WriteLine("Main Ended");
     }
 }

When We Run The Program, We Get The Output
 Main Started
 Singleton : Start
 Singleton : Start
 Singleton End. There are 1 objects
 DoWork 1
 Singleton End. There are 2 objects
 DoWork 2
 Main Ended

Solution #3: Singleton in C# – Lazy With Thread Safety

Luckily, C# has a simple construct to synchronize threads using the lock block. The lock block guarantees that only one thread will execute the code inside it at a given time. We can use the lock block to correct solution #2.

Solution #3: Singleton in C# – Lazy With Thread Safety
 using System.Threading;

 class Singleton {
     private Singleton() {
         System.Console.WriteLine("Singleton");
     }
     public void DoWork() {
         System.Console.WriteLine("DoWork");
     }

     public static Singleton Instance {
         get {
             lock (_locker) {                    // <== A
                 if (_instance == null) {        // <== D
                     _instance = new Singleton();// <== B
                 }
                 return _instance;
             }                                   // <== C
         }
     }
     private static Singleton _instance = null;
     private static object _locker = new object();
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }

What Do You Think?

Now there is guarantee that only one object will be created.

Suppose one thread wants to access the single object via the static property when the object does not exist yet. The thread tries to acquire the lock (A) and succeeds (since no other thread has the lock). So it starts to create the object but then gets suspended (B). Now another thread wants to access the single object via the static property; it tries to acquire the lock (A), fails (since the first thread already acquires the lock), and therefore gets suspended. Only when the first thread finishes to create the object and releases the lock (C) – The thread gets resumed – but now, the object already exists, and there is no need to create the object (D).

When We Run The Program, We Get The Output
 Main Started
 Singleton : Start
 Singleton End. There are 1 objects
 DoWork 1
 DoWork 2
 Main Ended

It seems to solve the problem. But as we know ‘All magic comes with a price’, every time we access the single object, we request a lock. Since locking pieces of code takes some time – this solution has performance impact.

Can you think how to improve it?

Solution #4: Singleton in C# – Lazy and thread safe with Minimal Locks

The synchronization problem occurs only when the object does not exist yet. When the object exists, we can simply return _instance static field. Using this observation, our next approach is to request the lock only when needed – when we creating the single object.

Singleton in C# – Lazy and thread safe with Minimal Locks
 class Singleton {
     private Singleton() {
         System.Console.WriteLine("Singleton");
     }
     public void DoWork() {
         System.Console.WriteLine("DoWork");
     }

     public static Singleton Instance {
         get {
             if (_instance != null) {
                 return _instance;
             }
             lock (_locker) {
                 if (_instance == null) {
                     _instance = new Singleton();
                 }
                 return _instance;
             }
         }
     }
     private static Singleton _instance = null;
     private static object _locker = new object();
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }

What Do You Think?

We still need to lock the piece of code that creates the object. Can you have a better solution?

Solution #5: Singleton in C# – Lazy and thread safe with Minimal Locks By CLR

In the first solution of the problem, the object has been created before it was needed. If we enclose this class in outer class and provide static property to access the object – the object will be created only when needed since static fields initializers are executed at most once in a given application domain when the class is first accessed.

Singleton in C# – Lazy and thread safe with Minimal Locks By CLR
 class Singleton {
     public class Inner {
         private Inner() {
             System.Console.WriteLine("Singleton");
         }
         public void DoWork() {
             System.Console.WriteLine("DoWork");
         }
         public static Inner _instance = new Inner();
     }

     public static Singleton.Inner Instance {
         get {
             return Inner._instance;
         }
     }
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }
When We Run The Program, We Get The Output
 Main
 Singleton
 DoWork

What Do You Think?

Nice , but it is too tricky and others may not understand this solution.

Solution #6: Singleton in C# – Lazy and Thread Safe With Builtin Lazy

csharp 4.0 introduces the Lazy<TT> for lazy instantiation as part of BCL. This class allow to defer the instantiation of an TT object until the time it is needed to save memory and execution time.

When the Value property of Lazy<TT> is first accessed – the TT object is created using initialization method given in the constructor or by the default constructor of TT. In the default mode , the initialization is thread safe – locks are used to ensure that only a single thread can initialize a TT instance – similar to solution 4.

We will use this class in current solution:

Singleton in C# – Lazy and Thread Safe With Builtin Lazy
 class Singleton {
     private Singleton() {
         Console.WriteLine("Singleton");
     }

     public void DoWork() {
         Console.WriteLine("DoWork");
     }

     public static Singleton Instance {
         get {
             return LazyInstance.Value;
         }
     }

     private static readonly Lazy<singleton> LazyInstance = new Lazy<Singleton>( () => new Singleton() );
 }

 class Program {
     static public void Main() {
         System.Console.WriteLine("Main");
         Singleton.Instance.DoWork();
     }
 }

How To Evaluate your solution for singleton in C#

We have seen 6 approaches for solving the problem. How does the interviewer evaluate your solution?

  • Whether your solution is correct –
  • Whether you can explain the solution.
  • Whether you understand and keep up with the environment you are working with and
  • Whether you foresee issues that may arise when using your solution.
  • Whether your solution is thread safe.
  • Whether your solution ensures that single object is created only when needed to save memory and execution time.
  • Whether your solution allows accessing the object as fast as possible.

Leave a Reply

Your email address will not be published. Required fields are marked *