Click here to Skip to main content
15,884,298 members
Articles / Programming Languages / C#

A Reusable Base Class for the Singleton Pattern in C#

Rate me:
Please Sign up or sign in to vote.
4.71/5 (21 votes)
5 Apr 2013CPOL3 min read 128.8K   54   58
This article presents a reusable base class for implementing singletons in C#.

Introduction

Let's face it: everybody loves singletons! Over the past few years I used a lot of these little buggers throughout all my projects. Recently I became tired of all the "copy & paste" and decided to write a reusable singleton base class for all my projects to come. Today I want to share the fruits of my work with you folks.

Background

The purpose of this base class is to reduce the coding effort when creating a new singleton to an absolute minimum. That does not only mean less finger-tiring coding work for you, but also makes your singletons much more readable and maintainable.

This base class is also thread safe, which is an important thing when it comes to singletons.

Using the code 

The code I'm presenting today consists of two classes:

  • SingletonBase is the base class, that every singleton will derive from.
  • SingletonExample is a very simple singleton, that shows how to derive from the base class.

And that's it. All of it. Pretty simple, huh? Now let's dive right in!

The example singleton 

I'll start by putting the cart before the horse. The code below shows a very simple singleton class that derives from our magic singleton base class (which will be shown later).

C#
/// <summary>
/// This class is an example of how to create a singleton based on the singleton base class.
/// </summary>
class SingletonExample : SingletonBase<SingletonExample>
{
  /// <summary>
  /// Gets or sets a string property.
  /// </summary>
  public string SomeString {get; set; }
 
  /// <summary>
  /// Initializes a new instance of the <see cref="Singleton"/> class.
  /// </summary>
  private SingletonExample()
  {
  }
}

Here are the three important points to learn from the example above:

  • The singleton derives from our base class SingletonBase.
  • We pass the singleton's class name as a type parameter (SingletonBase<SingletonExample>)).
  • We define a private constructor. This is important since we don't want anyone to actually instantiate our singleton.

As you can see the singleton itself is super-simple. All of the heavy lifting is done by the base class. So you can concentrate on implementing all those fancy methods and properties you have always dreamed of.

The singleton base class

Now let's get to the good stuff: our magic singleton base class!  Here we want to make sure that only one instance of our singleton exists at a time. And we want to do so in a thread safe way. Let's get there step by step:

C#
public abstract class SingletonBase<T> where T : class
{
  ...

Here we declare our singleton base class. As you can see this is a template class (that's where Mr. "T" comes from). This allows us to pass in the singleton's class as a type parameter (see the example above).

Next we have to answer an important question: how does the user access the singleton's instance? This problem is usually solved by providing a property called "Instance" (very creative, right?). This property returns the one and only instance of our singleton. Here's how we implement that:

C#
/// <summary>
/// Static instance. Needs to use lambda expression
/// to construct an instance (since constructor is private).
/// </summary>
private static readonly Lazy<T> sInstance = new Lazy<T>(() => CreateInstanceOfT());

/// <summary>
/// Gets the instance of this singleton.
/// </summary>
public static T Instance { get { return sInstance.Value; } }

I think the above lines of code deserve a little bit of explanation. First of all there is an static member called "sInstance". It holds the instance of our singleton, and is lazily initialized. This is comfortably achieved by using .NET 4.0's Lazy<T> class. And the best thing is: Lazy<T> is fully thread safe!

The second thing to notice is the mysterious method called "CreateInstanceOfT". Why do we need that? Why not simply call new? Well, that's due to the fact that our singleton has a private constructor (if you have forgotten, just scroll up a little bit to our SingletonExample class). Because of this we have to resort to a little trick: the Activator!

C#
/// <summary>
/// Creates an instance of T via reflection since T's constructor is expected to be private.
/// </summary>
/// <returns></returns>
private static T CreateInstanceOfT()
{
  return Activator.CreateInstance(typeof(T), true) as T;
}

With the help of this little method we can now instantiate our singleton class, although its constructor is private. Neat, huh?

The whole singleton base class

And that's it! Now you've seen the whole singleton base class. Here's what all those bits look like put together to a complete class, ready for copy & paste:

C#
/// <summary>
/// A base class for the singleton design pattern.
/// </summary>
/// <typeparam name="T">Class type of the singleton</typeparam>
public abstract class SingletonBase<T> where T : class
{
  #region Members

  /// <summary>
  /// Static instance. Needs to use lambda expression
  /// to construct an instance (since constructor is private).
  /// </summary>
  private static readonly Lazy<T> sInstance = new Lazy<T>(() => CreateInstanceOfT());

  #endregion

  #region Properties

  /// <summary>
  /// Gets the instance of this singleton.
  /// </summary>
  public static T Instance { get { return sInstance.Value; } }

  #endregion

  #region Methods

  /// <summary>
  /// Creates an instance of T via reflection since T's constructor is expected to be private.
  /// </summary>
  /// <returns></returns>
  private static T CreateInstanceOfT()
  {
    return Activator.CreateInstance(typeof(T), true) as T;
  }

  #endregion
}

Points of Interest 

As you might have noticed the Lazy<T> class is only available in .NET 4.0 and higher. If you are somehow forced by a higher power to use .NET versions below that, this code is not for you. Sorry!

History

I've rewritten the method CreateInstanceOfT based on the comments below. As people pointed out to me the Activator is faster than reflection when it comes to instantiating objects.

License

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


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSingletons and Thread Safety Pin
David A. Gray1-Aug-21 21:03
David A. Gray1-Aug-21 21:03 
GeneralMy vote of 5 Pin
David A. Gray1-Aug-21 20:43
David A. Gray1-Aug-21 20:43 
QuestionConstructor with arguments Pin
febo7413-Apr-20 6:29
febo7413-Apr-20 6:29 
Question5 Pin
#realJSOP4-Apr-20 23:52
mve#realJSOP4-Apr-20 23:52 
Questionupdated code for standardizations Pin
Member 140096135-Oct-18 21:07
Member 140096135-Oct-18 21:07 
"https://dotnetfiddle.net/8GGUa3">Domainaton -inherited singleton | C# Online Compiler | .NET Fiddle[^]


using System;

//  Domainaton , a singleton prototype class as a derived singleton that can be templated for any business logic or....
//  used as a  common singleton that mutates based on a business module/component / domain, how ever you divide your ...
//  business enterprise system.
// best practice: Like singletons, they must instiate themselves and only one time per lifetime of "app".. not component :)
//                Cannot instiate or change localized singleton (domainaton) at any time... no access to (reflection? well. dont)
//                Base class is a template abstract class and privatized, cannot use as singleton itself
//                template: You can inherit and set constructor to only private.. or protected to extend futher
//                          will check in future, but must set initializer with an action to return new instiation.
//                          any added function to recall new function from child scope is illegal to singleton pattern
//                          senior devs - please hide any domainaton classes privately within one service or class to protect
//                                        only release access to Instance through a safe property of the class / service
//                business global: ex: logging, printing, API calls, common functions can be first level  as domainatons.
//                business domains or app divisions: inherit from first level as domainatons based on business model.2nd level
//                                                   rule of thumb: first level must hide access to Instance as protected.. and
//                                                                  application level services can be it's own business domain in 2nd level                                                                    
// 3rd levels or higher will be antipattern in most application developing models



public abstract class singleton<T>
{
	protected singleton()	{}

	private static T instance;
	private static Func<T> act;
	private static readonly object padlock = new object ();
	
	protected static void initializer(Func<T> action)	{act = action;	}

	protected static T Instance
	{
		get
		{
			lock (padlock)
			{
				if (singleton<T>.instance == null) {	instance = act(); }
				return singleton<T>.instance;
			}
		}
	}
}

public class Printer : singleton<Printer>
{
	private Printer() {	}

	public new static Printer Instance
	{
		get
		{
			Printer.initializer(() =>
			{
				Console.WriteLine("System: Printer Singleton Initialized");
				return new Printer();
			});
			return singleton<Printer>.Instance;
	    }
	}

	public void print(string m)	{	Console.WriteLine("To Printer; " + m);	}
}

public class Logger : singleton<Logger>
{
	private Logger() {}

	public new static Logger Instance
	{
		get
		{
			Logger.initializer(() =>
			{
				 Console.WriteLine("System: Logger Singleton Service initiated");
				 return new Logger();
			});
			return singleton<Logger>.Instance;
			
		}
	}
	public void log(string m)	{	Console.WriteLine("Logger: " + m);	}
}


public static class Program
{
	public static void Main()
	{
		Logger.Instance.log("test loegger");
		Logger.Instance.log("hello logger");
		Printer.Instance.print("hello printer");
		Logger.Instance.log("hello logger again");
		
		// singleton<Logger>.Instance.log("logging");  access hidden :) must create singleton
		//Logger l = new Logger(); access hidden, cannot initialize :)
	}
}

Suggestionthis actually works 1 Pin
Member 140096135-Oct-18 20:27
Member 140096135-Oct-18 20:27 
Questionderived class Pin
Member 339579429-Jul-14 9:19
Member 339579429-Jul-14 9:19 
QuestionException: Pin
CCB201013-Nov-13 10:17
CCB201013-Nov-13 10:17 
AnswerRe: Exception: Pin
Kees van Spelde24-Jan-15 5:12
professionalKees van Spelde24-Jan-15 5:12 
GeneralRe: Exception: Pin
Member 1242634630-Mar-16 6:08
Member 1242634630-Mar-16 6:08 
AnswerRe: Exception: Pin
#realJSOP4-Apr-20 23:51
mve#realJSOP4-Apr-20 23:51 
GeneralMy vote of 5 Pin
_Nizar19-Aug-13 2:36
_Nizar19-Aug-13 2:36 
Questionfull defination of SingletonExample Pin
shah jony22-May-13 20:51
shah jony22-May-13 20:51 
AnswerRe: full defination of SingletonExample Pin
Boris Brock11-Jun-13 21:24
Boris Brock11-Jun-13 21:24 
GeneralMy vote of 5 Pin
Member 84402889-Apr-13 2:21
Member 84402889-Apr-13 2:21 
QuestionSingleton Pin
geoyar8-Apr-13 9:20
professionalgeoyar8-Apr-13 9:20 
AnswerRe: Singleton Pin
Boris Brock8-Apr-13 21:58
Boris Brock8-Apr-13 21:58 
GeneralRe: Singleton Pin
geoyar9-Apr-13 8:44
professionalgeoyar9-Apr-13 8:44 
GeneralRe: Singleton Pin
teejay879-Apr-13 21:31
teejay879-Apr-13 21:31 
GeneralRe: Singleton Pin
geoyar10-Apr-13 10:27
professionalgeoyar10-Apr-13 10:27 
GeneralRe: Singleton Pin
teejay8710-Apr-13 21:17
teejay8710-Apr-13 21:17 
GeneralRe: Singleton Pin
geoyar11-Apr-13 9:28
professionalgeoyar11-Apr-13 9:28 
QuestionI don't like singleton Pin
Giuseppe Tollini5-Apr-13 8:31
Giuseppe Tollini5-Apr-13 8:31 
Suggestionsome thoughts and missing multithreading Pin
Thomas Haller5-Apr-13 6:00
Thomas Haller5-Apr-13 6:00 
GeneralRe: some thoughts and missing multithreading Pin
Boris Brock7-Apr-13 21:11
Boris Brock7-Apr-13 21:11 

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.