Without dependency injection, singletons were typically defined like so:
public class EarlySingleton {
public static EarlySingleton Instance { get; } = new EarlySingleton();
private EarlySingleton() { }
public void DoSomething() { /* something */ }
}
Note how the static Instance
property is initialized right from the start. You can read more about when exactly this initialization is run here.
If you want to delay the initialization until some client code actually uses the Instance
property, you could implement it like this:
public class LateSingleton {
private static LateSingleton? _instance;
public static LateSingleton Instance => _instance ?? (_instance = new LateSingleton());
private LateSingleton() { }
public void DoSomething() { /* something */ }
}
Late initialization makes sense if the singleton is rarely used and the instantiation is more involved as in the example (e.g. creating connections to other network services).
In modern .net apps one would use dependency injection (DI) and move the instance management out of your code and into the DI framework. Singleton classes will then look like regular classes:
public class Singleton {
public void DoSomething() { /* something */ }
}
It's then the kind of DI registration you use which ensures only a single instance will be created. The registration of an early-initialized singleton would look like this:
services.AddSingleton(new Singleton());
Registration of a late-initialized singleton would look like so:
services.AddSingleton(_ => new Singleton());
That is the actual creation of the singleton instance is deferred until some client code actually injects it.