The Singleton pattern is used to restrict the instantiation of a class to just one object. The singleton pattern must satisfy the single instance and global access principles.
A nice pattern, but: Why not use a global static variable?
The answer lies in the fact that this generally is not considered object-oriented. However, there are various different ways to implement the Singleton pattern, one more elegant than the other.
In the original implementation the client just creates a singleton and does its work, like setting and getting a ‘global’ variable.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Client { static void Main(string[] args) { // Create one singleton Singleton singleton1 = Singleton.CreateInstance(); // and store a value singleton1.Value = "Hello, world"; // Create 'another' singleton Singleton singleton2 = Singleton.CreateInstance(); // and read the value. Console.WriteLine(singleton2.Value); Console.ReadKey(); } } |
Instantiation of a singleton is done by calling its instantiation method. It checks if it is already instantiated. If this is the case, it returns the object. Otherwise, it creates one.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public sealed class Singleton { public string Value = "Unknown"; static Singleton instance = null; Singleton() { } public static Singleton CreateInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
The problem with this code is that it is not thread-safe. As a result, it is possible to create more instance. This is an unwanted side-effect of multithreading. You could use locking, but there is also a lock-free and thread-safe version.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public sealed class Singleton { public string Value = "Unknown"; static readonly Singleton instance = new Singleton(); // explicit static constructor static Singleton() { } Singleton() { } public static Singleton CreateInstance() { return instance; } } |
I certainly like this one! Read more about the pros and cons in the article Implementing the Singleton Pattern in C#, written by John Skeet.
Want to see a simplified real world example? Just rename Singleton to Logger and implement a method Log, like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Client { static void Main(string[] args) { Logger logger = Logger.CreateLogger(); // log an important message logger.Log("Hello, world"); Console.ReadKey(); } } public sealed class Logger { public void Log(string message) { Console.WriteLine(message); } static readonly Logger logger = new Logger(); static Logger() { } Logger() { } public static Logger CreateLogger() { return logger; } } |
See? The Singleton is a powerful pattern if you implement it correctly.
This post is part of my series on the foundational design patterns in C#. In this series we explore the ancient design patterns and their use in real-world programming situations of today. In these series of posts we explore each pattern and look at simplified real-world C# code that implements the pattern.
Comments