The reason is very tied to implementation details.
For example, see the following simple and wrong singleton implementation in C#:
public class A
{
private static readonly A _instance;
static A()
{
_instance = new A();
}
public static A Instance => _instance;
public string Name { get; set; }
}
Now let's derive it into B
:
public class B : A
{
public string Surname { get; set; }
}
What would happen if you call B.Current
static property?
// ERROR! Surname isn't a property of A
string surname = B.Instance.Surname;
One of main drawbacks of not sealing a singleton class is that static members are still accessible on derived classes. That is, it's very confusing that you can access Instance
property on B
but it returns the singleton instance of A
.
You can't take this risk and one simple and yet powerful way of avoiding confusion is sealing A
. Since A
is a singleton implementation, you don't want to be inheritable, because derived classes can't expand the singleton.
If you're agree with the point of avoiding the described situation, a more convenient implementation of singleton could be:
// SEALED
public sealed class A
{
private static readonly A _instance;
// Avoid that other code excepting A class members
// can instantiate A
private A() {}
static A()
{
_instance = new A();
}
public static A Instance => _instance;
public string Name { get; set; }
}
About the criticism of using a static read-only field instead of lazy-loading the single instance...
I've also checked that some seem to be against my proposal of instantiating the single instance as part of static constructor or using a field initializer. I could also simplify the implementation as follows:
public sealed class A
{
private A() {}
private static readonly A _instance = new A();
public static A Instance => _instance;
public string Name { get; set; }
}
And, as far as I know, it provides more advantages than instantiating the single A
instance in the Instance
static property getter:
...
private static A _instance;
public static A Instance
{
get
{
if(_instance == null) _instance = new A();
return _instance;
}
}
See this other Q&A to understand that static constructors are thread-safe: Is the C# static constructor thread safe?
In response to my answer's criticism about my sample singleton implementation
In some comment, Jon Skeet has said:
A singleton doesn't just provide a single instance - it ensure that
it's the only instance ever created. See
en.wikipedia.org/wiki/Singleton_pattern "In software engineering, the
singleton pattern is a design pattern that restricts the instantiation
of a class to one object." Your code doesn't do that.
It's not a good argument to invalidate my sample implementation. For example, most inversion of control containers let you configure the life-cycle to singleton of a given component and its implementation.
The whole implementation is a regular public class with no constraint preventing you from even configuring the same implementation for other component, or you can even instantiate the implementation with new
(i.e. new A()
) wherever you want.
But the so-called component is a singleton, because you've configured that it will be a single instance of the whole component across the entire AppDomain
.
Now let's see the Implementation section on Singleton pattern (Wikipedia):
Implementation of a singleton pattern must satisfy the single instance
and global access principles. It requires a mechanism to access the
singleton class member without creating a class object and a mechanism
to persist the value of class members among class objects. The
singleton pattern is implemented by creating a class with a method
that creates a new instance of the class if one does not exist. If an
instance already exists, it simply returns a reference to that object.
To make sure that the object cannot be instantiated any other way, the
constructor is made private. Note the distinction between a simple
static instance of a class and a singleton: although a singleton can
be implemented as a static instance, it can also be lazily
constructed, requiring no memory or resources until needed.
Comparing this implementation description with my sample implementation, I would say that the following implementation is a singleton:
public sealed class A
{
private A() {}
private static readonly A _instance = new A();
public static A Instance => _instance;
public string Name { get; set; }
}
- You can't publicly instantiate it.
- It can be only a single instance within a given
AppDomain
.
- It provides global access to itself.
- It implements a method to create the single instance, but it's using C# specific syntax and syntactic sugar.
- It's lazily instantiated when the static
Instance
property is first instantiated. This is how static constructors and static class field initializers work in C#.
Now let's analyze the inversion of control container case:
- Usually components are defined by interfaces and abstract classes. Few times are concrete classes. Thus, you can't instantiate an abstract class or interface.
- There's no
Instance
static property, but the implementation description on Wikipedia says: [...] It requires a mechanism to access the singleton class member without creating a class object and a mechanism to persist the value of class members among class objects. The fact that you can implement this with an Instance
static property of singleton class is just an implementation detail on some programming languages. The pattern description is fine with how inversion of control containers work. Either something like service locator anti-pattern or just dependency injection, are mechanisms that provide the single instance.
- With inversion of control containers you can access singletons globally. For example,
Container.Resolve<ISomeComponentConfiguredAsSingleton>()
. BTW, excepting on few cases, service locator is considered an anti-pattern.
- Most inversion of control containers can provide custom factories where you can define how the component implementation will be instantiated.
- Based on previous point, you can easily implement a factory with lazy-loading.
In summary, I would say that in my particular case I've already left my ortodoxy and I assume that design patterns are abstract definitions to implement solutions to common problems and most are and should be language-agnostic. As long as my code works as the description of a given design pattern for me is enough to consider I'm implementing the whole design pattern. You can even implement singleton without calling your class WhateverSingleton.