I started my design patterns learning with a singleton class. Could you explain me the difference between the following Instance property implementations?
public class Singleton
{
private static Singleton _instance;
public static Singleton Instance => _instance ?? (_instance = new Singleton());
}
public class Singleton
{
public static Singleton Instance => _instance.Value;
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
}
CodePudding user response:
public class Singleton
{
private static Singleton _instance;
public static Singleton Instance => _instance ?? (_instance = new Singleton());
}
The above implementation of sigleton is not thred safe. As static variables are always shared by all the threads in the process. So two threads can evaluate that _instance
is equal to null and two threads will create two instances of Singleton which is not eligible.
The following implementation is thread safe as it uses Lazy<T>
:
public class Singleton
{
public static Singleton Instance => _instance.Value;
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
}
You can use one of the simple constructors whose default behavior is to create a thread-safe Lazy object, so that only one instance of the lazily instantiated object is created no matter how many threads try to access it.
CodePudding user response:
Singleton pattern has evolved quite a bit since is first presentation in the GoF book. The classical implementation of the pattern requires a static way to access the instance, example given:
public class Singleton
{
public static Singleton Instance => ....
public virtual void CallMe() { ... }
}
However this approach has a big drawback when it's time to write tests. A "Client" class would reference the singleton in the following way:
public class Client
{
public void DoSomething()
{
Singleton.Instance.CallMe();
}
}
With this shape it is impossible to mock the implementation of the CallMe method and write a unit test for the DoSomething method.
What should be done to realize a singleton instance of a class is to leave it up to the IoC container, in this way:
public class Singleton
{
//no static properties
public virtual void CallMe() { ... }
}
public class Client
{
private readonly Singleton _singleton;
public Client(Singleton singleton)
{
_singleton = singleton;
}
public void DoSomething() => _singleton.CallMe();
}
//where you declare your services
builder.Services.AddSingleton<Singleton>();
builder.Services.AddScoped<Client>();