Have you ever faced the challenge of implementing a singleton in C# during a software developer interview? At first glance, this might seem like a straightforward question—a basic design pattern that many developers are familiar with. However, don’t let its simplicity fool you! This seemingly easy question can quickly become a minefield if you overlook the finer details.
When preparing your answer, it’s crucial to consider what the interviewer is truly looking for. Did you simply provide a basic implementation, or did you delve deeper into the nuances of the singleton pattern? Interviewers often use this question to gauge your understanding of important concepts such as thread safety, lazy initialization, and the implications of using a singleton in a multi-threaded environment.
Let’s take a closer look at what the interviewer truly wants to hear when they ask you to implement a singleton pattern and how they evaluate your solution.
Understanding the Singleton Pattern
The first and foremost thing a candidate needs to grasp is the essence of the singleton pattern. Understanding this foundational concept is crucial, especially in a software development interview setting. 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.

How would you implement those requirements?
Solution #1: Singleton in C# – The Naive Solution
You can quickly fulfill the first requirement—adding a mechanism to create one and only one object—by implementing the singleton pattern effectively. Let’s break down the process and highlight some important considerations to make it more engaging.
To start, you’ll need to establish that the singleton class is responsible for creating its single instance. This is crucial because it ensures that no other class can interfere with the instantiation process. By taking ownership of the instance, the singleton class maintains full control over how and when the object is created.
The next step is to restrict instantiation from other classes. This is where the power of C# comes into play. The language offers robust access modifiers that enable you to safeguard your singleton class. By declaring the constructor as private, you ensure that only the class itself can create an instance. This means that no external class can instantiate the singleton, whether in constructors, methods, property getters and setters, or even static method 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(); */
}
To ensure that our class creates one and only one instance, an effective and straightforward solution is to declare a public readonly static field initialized with a new instance of the class. This approach not only aligns with the singleton pattern but also leverages the features of C# to maintain simplicity and clarity in our design.
By utilizing a public readonly static field, we guarantee that the application creates only one object. This is because static field initializers are executed just once in a given application domain. As a result, regardless of how many times other classes attempt to access this field, they will always refer to the same instance.
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 confidently say that the approach of using a public readonly static field to create a singleton instance is indeed a correct solution. It effectively ensures that only one instance of the class is created, and that instance is easily accessible throughout the application. However, let’s take a moment to critically evaluate this implementation.
While this solution works well in many scenarios, it has a notable drawback: the object is created at the start of the application, even if it may not be needed immediately. This premature instantiation can potentially impact the performance of the program, particularly if the object creation process is resource-intensive. For applications with heavy resource demands or those that require quick startup times, this could lead to unnecessary delays and inefficient memory usage. Can you think of another solution—a more refined approach that ensures the object is created only when needed? This is a crucial question to consider, especially in software design where resource efficiency and performance are key.
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();
}
}
Evaluate your solution for singleton in C#
We’ve explored six distinct approaches for implementing the singleton pattern, and now it’s crucial to consider how an interviewer evaluates your solution. This evaluation process is multifaceted and can reveal a lot about your understanding of software design principles and your problem-solving skills.
1. Correctness of the Solution [Whether your solution is correct]
First and foremost, the interviewer will assess whether your solution is technically correct. This means that the implementation must adhere to the principles of the singleton pattern, ensuring that only one instance of the class is created and that this instance is accessible globally. A correct solution will also properly handle the lifecycle of the singleton instance, so you should be prepared to justify your design choices.
2. Clarity of Explanation [Whether you can explain the solution]
Next, your ability to explain your solution is paramount. An interviewer wants to see if you can articulate your thought process clearly and logically. This includes discussing why you chose a particular approach over others, what trade-offs you considered, and how your solution fits within the larger context of the application. Being able to communicate complex ideas in an understandable way is a critical skill in software development, as it often leads to better collaboration with team members.
3. Understanding the Environment [Whether you understand and keep up with the environment]
The interviewer will also evaluate your familiarity with the development environment you’re working in. This includes knowledge of the programming language, relevant frameworks, and best practices in software design. Demonstrating an awareness of the environment can signal to the interviewer that you are adaptable and capable of working effectively within the team’s existing codebase and tool
4. Foreseeing Potential Issues [Whether you foresee issues that may arise when using your solution]
A key aspect of a strong developer is the ability to foresee potential issues that may arise from your solution. This includes identifying limitations, edge cases, and scenarios where your implementation might fail. For example, if you implemented a lazy-loaded singleton, can you discuss how it would handle multi-threaded access? Are there any scenarios where your design might lead to a race condition or create performance bottlenecks? Discussing these considerations shows that you are not only focused on immediate results but also on the long-term sustainability of your solution.
5. Thread Safety [Whether your solution is thread safe]
Another important factor is whether your solution is thread-safe. In multi-threaded applications, multiple threads may try to access the singleton instance simultaneously. If your solution does not account for this, you may end up with multiple instances of the singleton. Be prepared to explain how you would ensure thread safety, whether through locking mechanisms, using Lazy<T>
, or other strategies. This knowledge highlights your understanding of concurrent programming and your ability to create robust applications.
6. Memory and Execution Efficiency [Whether your solution ensures that single object is created only when needed to save memory and execution time]
The interviewer will be interested in how well your solution conserves memory and execution time. An ideal singleton implementation creates the object only when it is actually needed, thereby optimizing resource usage. This is especially crucial in large applications where resource consumption can significantly impact performance. Discuss how your chosen approach minimizes memory overhead and improves execution efficiency.
7. Access Speed [Whether your solution allows accessing the object as fast as possible]
Finally, the interviewer will evaluate how quickly the singleton instance can be accessed. In performance-critical applications, the time it takes to retrieve the singleton instance can make a substantial difference. Be ready to discuss how your implementation ensures fast access, whether through direct static properties, optimized data structures, or caching strategies.
Engaging the Interviewer
Throughout this discussion, remember that engaging with the interviewer is key. Ask them for their perspective on the trade-offs of different implementations or how they handle singleton patterns in their projects. This not only demonstrates your enthusiasm for the topic but also creates a collaborative atmosphere that can make the interview more enjoyable for both parties.
By considering these evaluation criteria, you can approach your interview with a comprehensive understanding of not just the implementation itself, but also the broader implications of your design choices. This level of insight will undoubtedly impress your interviewer and position you as a thoughtful and capable candidate.