Christopher Zenzel

Contents

Table of Contents

Share

Creating a Singleton Class in C#

two women sitting beside table and talking

Creating a singleton class in C# involves making sure that only one instance of the class can exist throughout the lifetime of the application. Here are the steps to create a thread-safe singleton class in C#:

  1. Private Constructor: Make the constructor private to prevent other classes from creating instances of the singleton class.

  2. Private Static Instance: Create a private static variable to hold the single created instance of the class. This ensures that the instance is shared across all usages.

  3. Public Static Method: Provide a public static method that returns the instance of the singleton. This method checks if an instance already exists; if not, it creates one.

  4. Thread Safety: Ensure that the creation of the instance is thread-safe. There are several ways to do this, but using Lazy<T> is one of the easiest and most efficient methods.

Here’s an example of how to implement a thread-safe singleton class using the Lazy<T> type:

public class Singleton
{
    // Private static readonly instance managed by the Lazy type
    private static readonly Lazy<Singleton> lazyInstance = new Lazy<Singleton>(() => new Singleton());

    // Public static property to access the instance
    public static Singleton Instance => lazyInstance.Value;

    // Private constructor to prevent instance creation
    private Singleton()
    {
        // Initialization code here
    }

    // Example method
    public void DoSomething()
    {
        Console.WriteLine("Executing an action in the singleton instance");
    }
}

How to Use

You can access the singleton instance from anywhere in your application like this:

Singleton.Instance.DoSomething();

Explanation

  • Lazy<T>: This class provides support for lazy initialization, meaning the Singleton instance is not created until it is needed. It also handles thread safety, ensuring that the instance is created only once, even if multiple threads attempt to access it at the same time.
  • Private Constructor: Ensures that objects cannot be created outside of the class.
  • Static Property Instance: Provides global access to the singleton instance.

This approach is simple, thread-safe, and efficient, as it does not create the singleton object until it’s actually needed. Additionally, the use of Lazy<T> ensures that the instantiation is properly synchronized across multiple threads without needing to use locks explicitly.

Without the Lazy<T>

If you prefer to implement a singleton in C# without using Lazy<T>, you can do so by manually handling the instantiation and ensuring thread safety with a lock mechanism. Below is an example of how you can implement a thread-safe singleton class in C# using the sealed keyword and a private static variable with double-check locking:

Singleton Implementation without Lazy<T>

public sealed class Singleton
{
    // Static variable to hold the instance of the class
    private static Singleton instance = null;

    // Object to lock on, rather than locking on the type itself, for added security
    private static readonly object lockObject = new object();

    // Public static property to access the instance
    public static Singleton Instance
    {
        get
        {
            // First check to avoid locking each time the instance is requested
            if (instance == null)
            {
                // Lock to ensure only one thread can create the instance
                lock (lockObject)
                {
                    // Double-check to ensure instance was not created by another thread while waiting for the lock
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    // Private constructor to ensure no external instances can be created
    private Singleton()
    {
        // Optional: Initialization code here
    }

    // Example method to demonstrate operations
    public void DoSomething()
    {
        Console.WriteLine("Performing an action within the singleton instance");
    }
}

Key Components Explained

  • Private Constructor: Ensures that no instance can be created outside of the class.

  • Static Instance Variable: Holds the singleton instance. It starts uninitialized and only gets set inside the Instance property if it hasn’t been created yet.

  • Lock Object: A lockObject is used to lock the code block that creates the instance. This prevents multiple threads from creating multiple instances simultaneously.

  • Double-Check Locking: The Instance property uses a double-check locking pattern:
    1. The first check (outside the lock) improves performance by avoiding unnecessary locking once the instance is initialized.
    2. The second check (inside the lock) ensures thread safety.

Usage

You can access the singleton instance from your application like this:

Singleton.Instance.DoSomething();

This approach to implementing a singleton in C# ensures that the instance is created lazily and thread-safely, but without using the Lazy<T> infrastructure. This manual approach is a classic pattern, especially useful in scenarios where you need explicit control over the instantiation process or when dealing with environments where Lazy<T> might not be available.