Different example techniques of implementing Singleton patterns in C#.
using System;
using System.Threading;
namespace MsPressClr.Console
{
static class Singletons
{
public static class V1
{
public sealed class Singleton
{
// _lock is required for thread safety and having this object
// assumes that creating the singleton object is more expensive
// than creating a System.Object object and that creating the
// singleton object may not be necessary at all. Otherwise, it
// is more efficient and easier to just create the singleton
// object in a class constructor
private static readonly Object _lock = new Object();
// This field will refer to the one Singleton object
private static Singleton _instance;
// Private constructor prevents any code outside this class
// from creating an instance
private Singleton()
{
/* ... */
}
// Public, static method that returns the Singleton object
// (creating it if necessary)
public static Singleton GetInstance()
{
// If the Singleton was already created, just return
// it (this is fast)
if (_instance != null)
{
return _instance;
}
Monitor.Enter(_lock); // Not created, let 1 thread create it
if (_instance == null)
{
// Still not created, create it
Singleton localInstance = new Singleton();
// Save the reference in _instance. Ensures that the
// reference in 'localInstance' can be published into
// '_instance', only after the constructor has finished
// executing.
Volatile.Write(ref _instance, localInstance);
}
Monitor.Exit(_lock);
// Return a reference to the one Singleton object
return _instance;
}
}
}
public static class V2
{
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
// Private constructor prevents any code outside this class
// from creating an instance
private Singleton() { }
// Public, static method that returns the Singleton object
// (creating it if necessary)
public static Singleton GetInstance()
{
return _instance;
}
}
}
public static class V3
{
public sealed class Singleton
{
private static Singleton _instance;
// Private constructor prevents any code outside this class from
// creating an instance
private Singleton() { }
// Public, static method that returns the Singleton object
// (creating it if necessary)
public static Singleton GetInstance()
{
if (_instance != null)
{
return _instance;
}
// Create a new Singleton and root it if another thread
// didn't do it first
Singleton localInstance = new Singleton();
Interlocked.CompareExchange(ref _instance, localInstance, null);
// If this thread lost, then the second Singleton
// object gets GC'd
return _instance; // Return reference to the single object
}
}
}
// The FCL offers two types that encapsulate the patterns described in
// this section. The generic System.Lazy class looks like this
// (some methods are not shown).
public static void Go()
{
//
// public enum LazyThreadSafetyMode
// {
// None, // No threadsafety support at all (good for GUI apps)
// ExecutionAndPublication, // Uses the doublecheck locking technique
// PublicationOnly, // Uses the Interlocked.CompareExchange technique
// }
//
//
// Create a lazy initialization wrapper around getting the DateTime
Lazy<String> s = new Lazy<String>(() => DateTime.Now.ToString(), true);
System.Console.WriteLine(s.IsValueCreated); // Returns false because Value not queried yet
System.Console.WriteLine(s.Value); // The delegate is invoked now
System.Console.WriteLine(s.IsValueCreated); // Returns true because Value was queried
Thread.Sleep(10000); // Wait 10 seconds and display the time again
System.Console.WriteLine(s.Value); // The delegate is NOT invoked now; same result
//
// Also, being able to explicitly specify a synchronization object
// to the "EnsureInitialized" method's syncLock parameter allows
// multiple initialization functions and fields to be protected by
// the same lock.
String name = null;
LazyInitializer.EnsureInitialized(ref name, () => "Jeff");
System.Console.WriteLine(name); // Jeff
LazyInitializer.EnsureInitialized(ref name, () => "Richter");
System.Console.WriteLine(name); // Jeff
}
}
}