Singleton or static class?
The article will be useful primarily to developers who get lost in interviews when they hear the question: “What are the main differences between a singleton and a static class, when should one be used, and when is the other?” And it will certainly be useful for those developers who, at the word “pattern”, become discouraged or ask to stop expressing themselves :)
First, let's recall what a static class is and why it is needed. In any CLI-compliant language, the following paradigm for encapsulating global variables is used: there are no global variables . All members, including static ones, can be declared only within the framework of a class, and the classes themselves can ( but should not ) be grouped in any namespace. And if earlier it was necessary to imitate the behavior of a static class using a private constructor, then support for static classes at the platform level was added to the .NET Framework 2.0. The main difference between a static class and a regular, non-static one is that it is not possible to instantiate this class using the new operator. Static classes are essentially a kind of namespace - only unlike the latter, they are designed to hold static variables and methods and not types.
One of the generative patterns, first described by the Gang of Four (GoF). Ensures that the class has only one instance and provides a global access point to it . We will not consider in detail this pattern here, its purpose and the tasks it solves - there are a lot of detailed information about it on the network (for example, here and here ). I only note that singletones are thread safe and not, with simple and delayed initialization.
So what is the difference between these two entities, and when should they be used? I think it’s best to illustrate this in the following table:
Consider the criteria listed above in more detail.
Of course, we mean external access points, in other words, a public contract for the interaction of a class and its clients. It is more convenient to illustrate this using the code:
Singleton in the "canonical" implementation:
Static class:
The inheritance of static classes is simple - it is simply not supported at the language level. With Singleton, things are a little more complicated. For ease of use, many developers most often use the following pattern implementation:
And since multiple inheritance is forbidden in C # and in any CLI-compatible language, this means that we cannot inherit the Session class from any other useful class. The solution is to delagulate the access instance instance to the singleton:
Using interfaces allows you to achieve greater flexibility, increase the number of reusable code, increase testability, and, most importantly, avoid strong connectivity of objects. Static classes do not support inheritance in principle. Singleton, by contrast, fully supports interface inheritance, since it is a regular class. But it’s worth using this opportunity only if you intend to transfer a singleton instance as input parameters in mixed scenarios or broadcast it abroad. Mixed scenario example:
For static classes is not supported - you can pass except that the type, but it is useless in most situations, except in cases of application of the mechanism of reflection ( reflection ). Singleton is essentially an ordinary instance of an object:
The lifetime of a static class is limited by the lifetime of the domain - if we created this domain manually, then we indirectly control the lifetime of all its static types. We can control the singleton's lifetime as we wish. A striking example is delayed initialization:
You can also add a singleton instance delete operation:
This operation is extremely unsafe, since a singleton can store some state and therefore its re-creation may have undesirable consequences for its clients. If, however, the need for such a method arose (which most likely indicates design errors), then you should try to minimize the possible evil from its use - for example, make it closed and call the Instance property inside under certain conditions:
A static class does not support this feature because you cannot instantiate a static class. In the case of the singleton, everything looks simple:
True, in the variant with singleton aggregation, it is necessary to apply a not quite beautiful and, a little cumbersome solution:
Serialization applies only to class instances. A static class cannot have instances, so there is nothing to serialize in this case.
In any case, the choice of solution depends on the developer and on the specifics of the task he is solving. But, in any case, the following conclusions can be drawn:
The use of a singleton is justified when:
Using static classes is advisable when you do not need to implement any of the scripts listed for singleton. The main purpose of static classes is still in the grouping of logically similar methods, constants, fields and properties. For example: System.Math , System.BitConverter , System.Buffer , System.Convert , etc.
What is a static class?
First, let's recall what a static class is and why it is needed. In any CLI-compliant language, the following paradigm for encapsulating global variables is used: there are no global variables . All members, including static ones, can be declared only within the framework of a class, and the classes themselves can ( but should not ) be grouped in any namespace. And if earlier it was necessary to imitate the behavior of a static class using a private constructor, then support for static classes at the platform level was added to the .NET Framework 2.0. The main difference between a static class and a regular, non-static one is that it is not possible to instantiate this class using the new operator. Static classes are essentially a kind of namespace - only unlike the latter, they are designed to hold static variables and methods and not types.
What is Singleton?
One of the generative patterns, first described by the Gang of Four (GoF). Ensures that the class has only one instance and provides a global access point to it . We will not consider in detail this pattern here, its purpose and the tasks it solves - there are a lot of detailed information about it on the network (for example, here and here ). I only note that singletones are thread safe and not, with simple and delayed initialization.
And if there is no difference - why produce more?
So what is the difference between these two entities, and when should they be used? I think it’s best to illustrate this in the following table:
Singleton | Static class | |
---|---|---|
Number of Access Points | One (and only one) access point - Instance static field | N (depends on the number of public class members and methods) |
Class inheritance | Perhaps, but not always (more on that below) | Impossible - static classes cannot be instantiated, because you cannot instantiate objects of static classes |
Interface Inheritance | Perhaps without any restrictions | Impossible for the same reason that class inheritance is not possible |
Ability to pass as parameters | Possibly since Singleton provides a real object | Is absent |
Object lifetime monitoring | Perhaps - for example, delayed initialization (or on-demand creation ) | Impossible for the same reason that class inheritance is not possible |
Using an abstract factory to instantiate a class | maybe | Impossible due to lack of the possibility of creating an instance |
Serialization | maybe | Not applicable due to lack of instance. |
Consider the criteria listed above in more detail.
Number of Access Points
Of course, we mean external access points, in other words, a public contract for the interaction of a class and its clients. It is more convenient to illustrate this using the code:
Singleton in the "canonical" implementation:
public class Session
{
private static Session _instance;
// Реализация паттерна ...
public static Session Instance
{
get
{
// ...
return _instance;
}
}
public IUser GetUser()
{
// ...
}
public bool IsSessionExpired()
{
// ...
}
public Guid SessionID
{
get
{
// ...
}
}
}
Static class:
public static class Session
{
// Точка доступа 1
public static IUser GetUser()
{
// ...
}
// Точка доступа 2
public static bool IsSessionExpired()
{
// ...
}
// ...
// Точка доступа N
public static Guid SessionID
{
get
{
// ...
}
}
}
Class inheritance
The inheritance of static classes is simple - it is simply not supported at the language level. With Singleton, things are a little more complicated. For ease of use, many developers most often use the following pattern implementation:
public class Singleton where T : class
{
private static T _instance;
protected Singleton()
{
}
private static T CreateInstance()
{
ConstructorInfo cInfo = typeof(T).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[0],
new ParameterModifier[0]);
return (T)cInfo.Invoke(null);
}
public static T Instance
{
get
{
if (_instance == null)
{
_instance = CreateInstance();
}
return _instance;
}
}
}
public class Session : Singleton
{
public IUser GetUser()
{
// ...
}
public bool IsSessionExpired()
{
// ...
}
public Guid SessionID
{
get
{
// ...
}
}
}
And since multiple inheritance is forbidden in C # and in any CLI-compatible language, this means that we cannot inherit the Session class from any other useful class. The solution is to delagulate the access instance instance to the singleton:
public class Session : CoreObject
{
private Session()
{
}
public static Session Instance
{
get
{
return Singleton.Instance;
}
}
}
Interface Inheritance
Using interfaces allows you to achieve greater flexibility, increase the number of reusable code, increase testability, and, most importantly, avoid strong connectivity of objects. Static classes do not support inheritance in principle. Singleton, by contrast, fully supports interface inheritance, since it is a regular class. But it’s worth using this opportunity only if you intend to transfer a singleton instance as input parameters in mixed scenarios or broadcast it abroad. Mixed scenario example:
// Этот класс является синглтоном и реализует интерфейс ISession
public class Session: CoreObject, ISession
{
private Session()
{
}
public static Session Instance
{
get
{
return Singleton.Instance;
}
}
}
// Этот класс не является синглтоном и вообще может быть объявлен и реализован в другой сборке
// полностью скрывая детали реализации
public class VpnSession : ISession
{
}
public interface ISessionManager
{
ISession GetSession(Guid sessionID);
// Принимает интерфейс ISession, следуя принципам уменьшения связности
bool IsSessionExpired(ISession session);
}
Ability to pass as parameters
For static classes is not supported - you can pass except that the type, but it is useless in most situations, except in cases of application of the mechanism of reflection ( reflection ). Singleton is essentially an ordinary instance of an object:
// ...
ISessionManager _sessionManager;
// ...
bool isExpired = _sessionManager.IsSessionExpired(Session.Instance);
Object lifetime monitoring
The lifetime of a static class is limited by the lifetime of the domain - if we created this domain manually, then we indirectly control the lifetime of all its static types. We can control the singleton's lifetime as we wish. A striking example is delayed initialization:
public class Singleton where T : class
{
// ...
public static T Instance
{
get
{
if (_instance == null)
{
// Создание "по требованию"
_instance = CreateInstance();
}
return _instance;
}
}
}
You can also add a singleton instance delete operation:
public class Singleton where T : class
{
// ...
public static T Instance
{
// ...
}
// Очень опасная операция!
public void RemoveInstance()
{
_instance = null;
}
}
This operation is extremely unsafe, since a singleton can store some state and therefore its re-creation may have undesirable consequences for its clients. If, however, the need for such a method arose (which most likely indicates design errors), then you should try to minimize the possible evil from its use - for example, make it closed and call the Instance property inside under certain conditions:
public class Singleton where T : class
{
// ...
public static T Instance
{
get
{
if (!IsAlive)
{
// Удаление по условию
RemoveInstance();
}
if (_instance == null)
{
// Создание "по требованию"
_instance = CreateInstance();
}
return _instance;
}
}
private void RemoveInstance()
{
_instance = null;
}
}
Using an abstract factory to instantiate a class
A static class does not support this feature because you cannot instantiate a static class. In the case of the singleton, everything looks simple:
public interface IAbstractFactory
{
T Create();
bool IsSupported();
}
public class Singleton where T : class
{
private static T _instance;
private static IAbstractFactory _factory;
protected Singleton(IAbstractFactory factory)
{
_factory = factory;
}
public static T Instance
{
get
{
if (_instance == null)
{
_instance = _factory.Create();
}
return _instance;
}
}
}
// Вариант с прямым наследованием от синглтона
public class Session : Singleton
{
protected Session()
: base(new ConcreteFactory())
{
}
// ...
}
True, in the variant with singleton aggregation, it is necessary to apply a not quite beautiful and, a little cumbersome solution:
public class Session : CoreObject, ISession
{
private class SessionSingleton : Singleton
{
protected SessionSingleton()
: base(new ConcreteFactory2())
{
}
}
private Session()
: base(new CoreContext())
{
}
public static Session Instance
{
get
{
return SessionSingleton.Instance;
}
}
// ...
}
Serialization
Serialization applies only to class instances. A static class cannot have instances, so there is nothing to serialize in this case.
So what to use Singleton or Static class?
In any case, the choice of solution depends on the developer and on the specifics of the task he is solving. But, in any case, the following conclusions can be drawn:
The use of a singleton is justified when:
- It is necessary to inherit classes or interfaces or to delegate the construction of objects to the factory
- Use of class instances required
- It is necessary to control the lifetime of the object (although this is a very rare task for a singleton)
- It is necessary to serialize the object (such a task is hypothetically possible, but it is difficult to imagine use cases)
Using static classes is advisable when you do not need to implement any of the scripts listed for singleton. The main purpose of static classes is still in the grouping of logically similar methods, constants, fields and properties. For example: System.Math , System.BitConverter , System.Buffer , System.Convert , etc.