Singletons in C#
In many ways software engineering strives to strike a balance between testability, scalability (and performance), execution logic and symmetry (in the case of distributed algorithms), and semantics (easily understood and usable) while remaining efficient in the use of system resources. In my opinion, these major variables are all of equal weighting; these define the elegance of a particular design.
For this discussion, I’d like to focus on two of the variables: testability and efficient use of system resources.
Garbage Collection
As a prerequisite, I recommend you read this article on how garbage collection works at a high-level in the Microsoft .NET Framework. If you don’t have time to read the article then do take time to read the next paragraph.
The following is a good idea what sorts of things we should try to avoid to get the best performance out of the garbage collector:
- Too many allocations.
- Too-large allocation.
- Too many pointers.
- Too many roots.
- Too many object writes.
- Too many Almost-Long-Life objects.
- If you implement IDisposable then suppress finalization to reduce costs.
Dependency Injection (DI)
A foundational pattern in xUnit testing is Dependency Injection (DI), which is a form of Inversion of Control (IoC). Specifically, DI is a programming technique where the implementation of one class is actually performed partially by another. Inversion of Control is where a program gives up control of its own execution and simply responds to requests made of it. In the same way, a class using dependency injection gives up control over some of its implementation and lets the injected class do the work.
Three types of DI exist; constructor injection, property injection, and method call injection. Constructor injection is a pattern whereby dependencies are “injected” into an object during construction. Whereas, property injection uses set operations [on properties] to inject dependencies, which obviously occurs after construction. Method call injection simply uses method calls to inject dependencies.
Several libraries exist in each category. For example, the Castle Project and ObjectBuilder both provide supports for constructor and property injection. As a side note, the Microsoft Enterprise Library uses ObjectBuilder internally as its DI container framework. Unity is Microsoft’s latest offering from the Pattern’s & Practices team. Unity offers property, constructor, and method call injection.
Static Classes
A static class does not have any instance-level members (including constructors) and is defined by the static modifier. The compiler in turn marks a static class as sealed and automatically creates a private constructor.
The main features of a static class are:
- They only contain static members.
- They cannot be instantiated.
- They are sealed.
- They cannot contain Instance Constructors.
Inherently in the context of unit (and integration) testing, static classes are not recommended due to the difficulty in implementing a thread-safe property injector DI pattern and being unable to use constructor DI. However, the advantage of a properly written static class is there’s only one object instance per AppDomain, which can be very efficient on the GC (see the Too Many Object Writes section of the previously referenced article).
This is a very important and annoying impedance that exists between the use of a constructor DI pattern and efficient use (and preservation) of system resources (in this case CPU and memory). We want to be testable and mindful of system resources.
Singletons
For purposes of this discussion, a singleton is a class that only allows a single instance of itself to be created within an AppDomain. The following are recommended singleton patterns.
Sidebar: Understand the Impact of Low-Lock Techniques in Multithreaded Apps
Pattern 1: Niladic Constructor
public class MySingleton {
private static readonly MySingleton _instance = new MySingleton();
private MySingleton() {
}
public static MySingleton Instance {
get { return _instance; }
}
}
Pattern 2: Niladic Constructor (Full Lazy Initialization)
public class MySingleton {
class Nested {
//
// Explicit static constructor to tell C# compiler not to mark
// type as BeforeFieldInit
//
static Nested() {
}
internal static readonly MySingleton Instance = new MySingleton();
}
private MySingleton() {
}
public static MySingleton Instance {
get { return Nested.Instance; }
}
}
Note, the following alternate method results in identical MSIL…
public class MySingleton {
static class Nested {
internal static readonly MySingleton Instance = new MySingleton();
}
private MySingleton() {
}
public static MySingleton Instance {
get { return Nested.Instance; }
}
}
A rather dated but still applicable discussion regarding the use of the double-check lock pattern written by Vance Morrison can be found here and provides insight into subtle nuances of the Microsoft .NET Framework memory model implementation. I’ve attached a PDF version of the discussion to this entry (at the bottom) in case the archive disappears.
Singleton
So back to the question at hand, which is how do I ensure my production code is testable and uses system resources efficiently as possible? I offer the Singleton class, which is defined as…
public sealed class Singleton where T : new() {
private static readonly T _instance = new T();
private Singleton() {
}
public static T Instance {
get { return _instance; }
}
}
With the Singleton class, we’re now able to use the following syntax in production code…
public class CustomerManager : BusinessLogicComponent {
private ICustomerRepository _repository;
//
// This constructor is required by the Singleton generic class.
//
[EditorBrowsable(EditorBrowsableState.Never)]
public CustomerManager()
: this(Singleton.Instance) {
}
//
// This constructor is to be used only by test projects.
//
[EditorBrowsable(EditorBrowsableState.Never)]
public CustomerProcessor(ICustomerRepository repository) {
_repository = repository;
}
public void Add(Customer customer) {
_repository.Add(customer);
}
}
…and the following syntax from test projects.
[Test]
public void Customer_Add_Succeeds() {
//
// Create a mock customer repository and inject it into the business
// logic component (manager).
//
ICustomerRepository repository = new MockCustomerRepository();
CustomerManager manager = new CustomerManager(repository)
//
// Perform remaining test operations.
//
}
Consumers of the CustomerManager business logic class in production simply use the Singleton class just as the CustomerManager does with the CustomerRepository class in its niladic constructor.
A final note on thread-safety. The singleton pattern does require all instance level methods to be thread-safe.
Conclusion
The Singleton class provides an implementation pattern to balance between two important variables in any design; that of testability and efficient use of system resources. I encourage you to experiment.