Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 43: Singleton Implementation

Section 43.1: Statically Initialized Singleton

public class Singleton

{

private readonly static Singleton instance = new Singleton(); private Singleton() { }

public static Singleton Instance => instance;

}

This implementation is thread-safe because in this case instance object is initialized in the static constructor. The CLR already ensures that all static constructors are executed thread-safe.

Mutating instance is not a thread-safe operation, therefore the readonly attribute guarantees immutability after initialization.

Section 43.2: Lazy, thread-safe Singleton (using Lazy<T>)

.Net 4.0 type Lazy guarantees thread-safe object initialization, so this type could be used to make Singletons.

public class LazySingleton

{

private static readonly Lazy<LazySingleton> _instance = new Lazy<LazySingleton>(() => new LazySingleton());

public static LazySingleton Instance

{

get { return _instance.Value; }

}

private LazySingleton() { }

}

Using Lazy<T> will make sure that the object is only instantiated when it is used somewhere in the calling code.

A simple usage will be like:

using System;

public class Program

{

public static void Main()

{

var instance = LazySingleton.Instance;

}

}

Live Demo on .NET Fiddle

Section 43.3: Lazy, thread-safe Singleton (using Double Checked Locking)

This thread-safe version of a singleton was necessary in the early versions of .NET where static initialization was not guaranteed to be thread-safe. In more modern versions of the framework a statically initialized singleton is

GoalKicker.com – C# Notes for Professionals

177

usually preferred because it is very easy to make implementation mistakes in the following pattern.

public sealed class ThreadSafeSingleton

{

private static volatile ThreadSafeSingleton instance; private static object lockObject = new Object();

private ThreadSafeSingleton()

{

}

public static ThreadSafeSingleton Instance

{

get

{

if (instance == null)

{

lock (lockObject)

{

if (instance == null)

{

instance = new ThreadSafeSingleton();

}

}

}

return instance;

}

}

}

Notice that the if (instance == null) check is done twice: once before the lock is acquired, and once afterwards. This implementation would still be thread-safe even without the first null check. However, that would mean that a lock would be acquired every time the instance is requested, and that would cause performance to su er. The first null check is added so that the lock is not acquired unless it's necessary. The second null check makes sure that only the first thread to acquire the lock then creates the instance. The other threads will find the instance to be populated and skip ahead.

Section 43.4: Lazy, thread safe singleton (for .NET 3.5 or older, alternate implementation)

Because in .NET 3.5 and older you don't have Lazy<T> class you use the following pattern:

public class Singleton

{

private Singleton() // prevents public instantiation

{

}

public static Singleton Instance

{

get

{

return Nested.instance;

}

}

private class Nested

{

GoalKicker.com – C# Notes for Professionals

178

//Explicit static constructor to tell C# compiler

//not to mark type as beforefieldinit

static Nested()

{

}

internal static readonly Singleton instance = new Singleton();

}

}

This is inspired from Jon Skeet's blog post.

Because the Nested class is nested and private the instantiation of the singleton instance will not be triggered by accessing other members of the Sigleton class (such as a public readonly property, for example).

GoalKicker.com – C# Notes for Professionals

179