Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#

How Do You Structure Your Singletons?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
1 Aug 2013CPOL4 min read 16.8K   9   12
How to structure your Singletons.

Background

I'll skip over the discussion about why many people hate singletons (that's a whole separate can of worms, but worth mentioning) because in the end, it's not going to get rid of them. A singleton is a design pattern that ensures only one instance of the singleton object can ever be constructed. Often, singletons are made to be "globally accessible" in an application (although, I'm still not confident that this is actually part of the definition of a singleton). There are a handful of different ways to implement singletons... so what are they?

The Not-Thread-Safe-Singleton

With singletons, the main goal is to ensure that only one instance can be created. Below is a snippet of singleton code that works, but is not guaranteed in a multi-threaded environment:

C#
internal class NotThreadSafeSingleton
{
    private static NotThreadSafeSingleton _instance;

    /// <summary>
    /// Prevents a default instance from being created.
    /// </summary>
    private NotThreadSafeSingleton()
    {
    }

    /// <summary>
    /// Gets the singleton instance (not thread safe).
    /// </summary>
    internal static NotThreadSafeSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new NotThreadSafeSingleton();
            }

            return _instance;
        }
    }
}

The key take away point here is that if this singleton class is accessed across multiple threads, it is possible that two threads perform the null check and begin the constructor at the same time. Because this block of code is not protected with a lock a race condition occurs.

The Thread-Safe Singleton

If the key problem with the first example was that it is not safe across threads due to lack of locking... Then it should be pretty obvious that we just need to lock!

C#
internal class ThreadSafeSingleton
{
    private static readonly object _instanceLock = new object();
    private static ThreadSafeSingleton _instance;

    /// <summary>
    /// Prevents a default instance of the class from being created.
    /// </summary>
    private ThreadSafeSingleton()
    {
    }

    /// <summary>
    /// Gets the singleton instance (thread safe).
    /// </summary>
    internal static ThreadSafeSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_instanceLock)
                {
                    if (_instance == null)
                    {
                        _instance = new ThreadSafeSingleton();
                    }
                }
            }

            return _instance;
        }
    }
}

That looks sort of weird though, doesn't it? The double-check serves two purposes: a required check for thread safety and a performance optimization! The check inside of the lock actually guarantees that a single instance is created across threads. However, the check outside is an optimization because after the first instance is created, there's overhead to acquiring the lock just to check if the instance exists.

For what it's worth, this is actually my preferred method of coding singletons. I've been coding them like this for a while in C#, and I find they work nicely. However, I stumbled upon a posting by John Skeet (essentially, a programming god) who's done a much better job at documenting singleton patterns than I have. In his analysis, he actually notes some drawbacks to this pattern. Firstly, he mentions it doesn't work in Java, which is a great point. I primarily code in C#, but this is still important to acknowledge. Secondly, he calls upon ECMA CLI memory specifications that state it's not guaranteed to actually be thread safe without memory barriers. This was slightly out of my comfort zone, so I admit I still have yet to look into this. Finally, I do agree that the pattern is easy to get wrong for more junior developers and as for performance, I've never considered it an issue at all. (Hopefully by now you've come back from his write-up to finish mine!)

Generic Singleton: A Quick Hand At It...

One of the big drawbacks I saw with the thread safe singleton implementation that I favour is the amount of boilerplate code. Every time I want a new singleton class, I have to code basically everything you see in the previous section. It's redundant and I don't want to do it. I can hear the singleton-haters saying "so STOP making them!" but as I said, that's a discussion for a later post.

I decided I'd have a quick attempt at making a generic singleton! Check out my implementation below (and be warned that I flipped some condition checks in order to try and reduce the width of the text to fit nicely here...):

C#
internal class Singleton<T> where T : class
{
    private static readonly object _instanceLock = new object();
    private static T _instance;

    /// <summary>
    /// Prevents a default instance of the class from being created.
    /// </summary>
    private Singleton()
    {
    }

    /// <summary>
    /// Gets the singleton instance (thread safe).
    /// </summary>
    internal static T Instance
    {
        get
        {
            if (_instance != null)
            {
                return _instance;
            }

            lock (_instanceLock)
            {
                if (_instance != null)
                {
                    return _instance;
                }

                const string SINGLETON_EXCEPTION_MSG =
                        "A single private parameterless constructor is required.";

                var constructors = typeof(T).GetConstructors(
                    BindingFlags.Public |
                    BindingFlags.CreateInstance |
                    BindingFlags.Instance);

                if (constructors.Length > 0)
                {
                    throw new Exception(SINGLETON_EXCEPTION_MSG);
                }

                constructors = typeof(T).GetConstructors(
                    BindingFlags.NonPublic |
                    BindingFlags.CreateInstance |
                    BindingFlags.Instance);

                if (constructors.Length != 1 ||
                    constructors[0].GetParameters().Length > 0 ||
                    !constructors[0].IsPrivate)
                {
                    throw new Exception(SINGLETON_EXCEPTION_MSG);
                }

                _instance = (T)constructors[0].Invoke(null);
                return _instance;
            }
        }
    }
}

Doesn't look to shabby, right? You simply need to create a class with *only* a private constructor and everywhere you want access to the singleton, you say Singleton<MyType>.Instance. It's that easy.

Unfortunately, this isn't as good as I initially hoped. Can you think of an example where this approach breaks down? My hint to you is everything you need to break it is posted right here. Unfortunate really because I thought I was onto something good there! :) In reality, it may not be a bad foundation for your singleton implementations if you don't like the boiler plate code I listed above.

Summary

There are many ways to write singletons, and some are clearly more robust than others. I've only shown you a couple here, but John Skeet's post has a handful more which he prefers. It's hard to argue with what he's written (although, I'm not actually fond of his fourth implementation which he likes) because he's done a great analysis on them. In the end, it's important to know what a singleton actually is, when you might want to use it, and what the differences actually mean in various implementations.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader Microsoft
United States United States
I'm a software engineering professional with a decade of hands-on experience creating software and managing engineering teams. I graduated from the University of Waterloo in Honours Computer Engineering in 2012.

I started blogging at http://www.devleader.ca in order to share my experiences about leadership (especially in a startup environment) and development experience. Since then, I have been trying to create content on various platforms to be able to share information about programming and engineering leadership.

My Social:
YouTube: https://youtube.com/@DevLeader
TikTok: https://www.tiktok.com/@devleader
Blog: http://www.devleader.ca/
GitHub: https://github.com/ncosentino/
Twitch: https://www.twitch.tv/ncosentino
Twitter: https://twitter.com/DevLeaderCa
Facebook: https://www.facebook.com/DevLeaderCa
Instagram:
https://www.instagram.com/dev.leader
LinkedIn: https://www.linkedin.com/in/nickcosentino

Comments and Discussions

 
SuggestionNo need for reflection Pin
Richard Deeming12-Aug-13 8:15
mveRichard Deeming12-Aug-13 8:15 
GeneralRe: No need for reflection Pin
Dev Leader12-Aug-13 8:44
Dev Leader12-Aug-13 8:44 
SuggestionLazy<T> does the trick ? Pin
mike7530-Jul-13 11:01
mike7530-Jul-13 11:01 
GeneralRe: Lazy<T> does the trick ? Pin
Dev Leader30-Jul-13 11:10
Dev Leader30-Jul-13 11:10 
GeneralThoughts Pin
PIEBALDconsult24-Jul-13 7:58
mvePIEBALDconsult24-Jul-13 7:58 
GeneralRe: Thoughts Pin
Dev Leader24-Jul-13 8:32
Dev Leader24-Jul-13 8:32 
GeneralRe: Thoughts Pin
PIEBALDconsult24-Jul-13 8:45
mvePIEBALDconsult24-Jul-13 8:45 
GeneralRe: Thoughts Pin
Dev Leader24-Jul-13 9:58
Dev Leader24-Jul-13 9:58 
GeneralRe: Thoughts Pin
PIEBALDconsult24-Jul-13 10:00
mvePIEBALDconsult24-Jul-13 10:00 
GeneralRe: Thoughts Pin
Dev Leader24-Jul-13 11:42
Dev Leader24-Jul-13 11:42 
QuestionError in code example Pin
Bermin24-Jul-13 6:07
Bermin24-Jul-13 6:07 
AnswerRe: Error in code example Pin
Dev Leader24-Jul-13 6:10
Dev Leader24-Jul-13 6:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.