Press "Enter" to skip to content

How to implement the Singleton Design Pattern in C#

What is the Singleton Design Pattern?

The Singleton Design Pattern is used when you want every place in your program to use the same instance of a class.

Singletons are often used as a place to maintain shared data or cached values, or for objects that take a long time to initialize.

Why would you use a singleton?

In this example, we’ll use the Singleton design pattern for a class that would write log messages to a log file. There might be file-locking problems if multiple objects are trying to write to the same file. So, we want all logging to be handled through a single Logger object.

In this first image, we see how the code would look if each object instantiated its own Logger object, and each Logger object tried to write to the same LogFile.txt file.

With a singleton logger object, you don’t need to worry about several different objects trying to write to the same file, at the same time. Every object that has a Logger object has a reference to the exact some instance. So, we don’t need to worry about potential locking problems – since LogFile.txt is only ever used by one object.

Source code without the Singleton Design Pattern

This example code shows how someone could create Logger objects without using the Singleton pattern. This code would create a new Logger object for each Customer object.

namespace DesignPatternsCSharpNet6.Singleton.NonPattern;
public class Logger
{
    // Public constructor allows other classes
    // to instantiate a Logger object,
    // which allows multiple instances of Logger objects
    public Logger()
    {
    }
    public void WriteMessage(string message)
    {
        // Pretend we're writing the message to a file here.
    }
}
namespace DesignPatternsCSharpNet6.Singleton.NonPattern;
public class Customer
{
    private readonly Logger _logger;
    public string Name { get; }
    public Customer(string name)
    {
        _logger = new Logger();
        Name = name;
    }
    public void UpdateAddress(string streetAddress, string city)
    {
        // Pretend we update the database, and/or do other things here.
        // Now, write a log message, so we have a record of the update.
        _logger.WriteMessage($"Updated the address for: {Name} at: {DateTime.Now}");
    }
}

Source code with the Singleton Design Pattern (without locking)

Here’s one method to ensure the program only ever instantiates a single instance of the Logger class.

The first step is to make the Logger constructor private, so it can only be called by other code inside the class. Now, no other class will be able to instantiate a Logger object.

Second, we create a static GetLogger function in the Logger class. Static functions can be called without an instance of the class, so Logger.GetLogger() can be called from any class, at any time. And, when the other classes call Logger.GetLogger(), they’ll all get the one instance of the Logger object that is stored in the class-level s_logger variable.

NOTE: The s_logger backing variable must be static, because it is being used by the static GetLogger function.

namespace DesignPatternsCSharpNet6.Singleton.Pattern_StaticInitialization;
public sealed class Logger
{
    // Instantiating the s_logger here is thread-safe,
    // and eliminates needing to do manual locking.
    private static readonly Logger s_logger = new Logger();
    // Private constructor stops other classes
    // from instantiating a Logger object.
    private Logger()
    {
    }
    // This static methods returns the single Logger object,
    // which was instantiated on line 7.
    public static Logger GetLogger()
    {
        return s_logger;
    }
    public void WriteMessage(string message)
    {
        // Pretend we're writing the message to a file here.
    }
}
namespace DesignPatternsCSharpNet6.Singleton.Pattern_StaticInitialization;
public class Customer
{
    private readonly Logger _logger;
    public string Name { get; }
    public Customer(string name)
    {
        _logger = Logger.GetLogger();
        Name = name;
    }
    public void UpdateAddress(string streetAddress, string city)
    {
        // Pretend we update the database, and/or do other things here.
        // Now, write a log message, so we have a record of the update.
        _logger.WriteMessage($"Updated the address for: {Name} at: {DateTime.Now}");
    }
}

Source code with the Singleton Design Pattern (with double-locking)

This is an older method you may see for Singleton classes. These extra steps are not needed if you use the previous technique.

The lock(s_syncLock) will only allow one thread past it while s_syncLock is locked.

So, if two threads try to call Logger.GetLogger() at the same time, one will lock s_syncLock and continue. The second will wait until the code inside the “lock” block is completes.

Then, when the second thread gets the lock on s_syncLock, it double-checks if the s_logger is still null. In this case, the first thread instantiated s_logger, so the second thread sees s_logger is not null and does not try to instantiate s_logger again. It just returns the object that was created by the first thread.

namespace DesignPatternsCSharpNet6.Singleton.Pattern_DoubleLock;
public sealed class Logger
{
    private static Logger s_logger;
    private static readonly object s_syncLock = new object();
    // Private constructor stops other classes
    // from instantiating a Logger object.
    private Logger()
    {
    }
    // This static methods returns the single Logger object,
    // which it instantiates, using a lock, to prevent threading issues.
    public static Logger GetLogger()
    {
        if (s_logger == null)
        {
            lock (s_syncLock)
            {
                if (s_logger == null)
                {
                    s_logger = new Logger();
                }
            }
        }
        return s_logger;
    }
    public void WriteMessage(string message)
    {
        // Pretend we're writing the message to a file here.
    }
}
namespace DesignPatternsCSharpNet6.Singleton.Pattern_DoubleLock;
public class Customer
{
    private readonly Logger _logger;
    public string Name { get; }
    public Customer(string name)
    {
        _logger = Logger.GetLogger();
        Name = name;
    }
    public void UpdateAddress(string streetAddress, string city)
    {
        // Pretend we update the database, and/or do other things here.
        // Now, write a log message, so we have a record of the update.
        _logger.WriteMessage($"Updated the address for: {Name} at: {DateTime.Now}");
    }
}

Unsafe method to create a singleton (DO NOT USE)

In this version, the GetLogger function checks to see if the _logger object is null. If it is, the method creates a new instance of the Logger object, assigns it to the private static variable, and returns it. If the object is not null (it already exists), it will return it.

However, this version is unsafe, because multiple objects might call the GetLogger method at the same time. Due to timing/threading, two objects might do their check if _logger is null at the same time. So, they would both see the value is null, and both try to create a new Logger object.

In this small sample project, that isn’t a real problem. But, if your program expects a single instance of the Logger object, this could lead to problems – ones that probably won’t be detected until the program is run in “the real world”.

I’m only posting this code so you can be familiar with it – in case you see it in an existing program.

This code is an image, so no one can copy-paste it into a program.

Alternatives to the Singleton pattern

An alternative to using the Singleton pattern is to use a static property on a static class. But that usually leads to bad programming practices. After a while, those classes tend to hold a lot of unrelated information and objects.

It’s also difficult to do unit tests with static classes. With a singleton object, it’s easier to create “mock” objects to use in their place when unit testing.

Also, the latest versions of .NET often use dependency injection. In the setup code, you can designate classes as being Singletons and the dependency injection code will manage only ever creating one instance of that class type.

This source code is available at: https://github.com/CodingWithScott/DesignPatternsCSharpNet6

    Leave a Reply

    Your email address will not be published.

    This site uses Akismet to reduce spam. Learn how your comment data is processed.