CreationalC#verifiedVerified
Singleton Pattern in C#
Ensures a class has only one instance and provides a global point of access to it.
How to Implement the Singleton Pattern in C#
1Step 1: Define the Singleton class with a private constructor
public sealed class Singleton
{
private static readonly Lazy<Singleton> _instance =
new(() => new Singleton());
private Singleton() { }2Step 2: Provide static access to the single instance
public static Singleton Instance => _instance.Value;
// Business methods
public void DoSomething() =>
Console.WriteLine("Singleton method called");
}3Step 3: Verify only one instance exists
// Usage:
// var a = Singleton.Instance;
// var b = Singleton.Instance;
// Console.WriteLine(ReferenceEquals(a, b)); // Trueusing Microsoft.Extensions.Logging;
// [step] Thread-safe Singleton with async initialization and dispose
public sealed class DatabasePool : IAsyncDisposable
{
private static DatabasePool? _instance;
private static readonly SemaphoreSlim _lock = new(1, 1);
private readonly List<object> _connections = [];
private bool _isShutdown;
public record Config(
string Host = "localhost", int Port = 5432,
string Database = "app", int MaxConnections = 10);
private DatabasePool(Config config) => Configuration = config;
public Config Configuration { get; }
// [step] Async-safe singleton with double-checked locking
public static async Task<DatabasePool> GetInstanceAsync(
Config? config = null, CancellationToken ct = default)
{
if (_instance is not null) return _instance;
await _lock.WaitAsync(ct);
try
{
if (_instance is not null) return _instance;
var pool = new DatabasePool(config ?? new Config());
await pool.InitializeAsync(ct);
_instance = pool;
return _instance;
}
finally
{
_lock.Release();
}
}
// [step] Initialize the connection pool
private async Task InitializeAsync(CancellationToken ct)
{
for (var i = 0; i < Configuration.MaxConnections; i++)
{
ct.ThrowIfCancellationRequested();
_connections.Add(new { Id = i, Active = false });
await Task.Delay(1, ct); // Simulate async setup
}
}
// [step] Query with pool validation
public Task<object[]> QueryAsync(
string sql, object[]? parameters = null,
CancellationToken ct = default)
{
ObjectDisposedException.ThrowIf(_isShutdown, this);
ct.ThrowIfCancellationRequested();
return Task.FromResult<object[]>(
[new { sql, parameters, result = "mock" }]);
}
// [step] Cleanup with IAsyncDisposable
public async ValueTask DisposeAsync()
{
if (_isShutdown) return;
_isShutdown = true;
_connections.Clear();
_instance = null;
await Task.CompletedTask;
}
/// <summary>Reset for testing only.</summary>
public static void ResetInstance() => _instance = null;
}Singleton Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Singleton Pattern in the Real World
“Think of a country’s president. There can only be one at any time. When anyone needs to communicate with the president, they don’t create a new one—they access the existing one through the official channel (the static method).”