Introduction
The Singleton pattern ensures that a class has only one instance throughout an application and provides a single, globally accessible way to retrieve it.
Learn through examples of common use cases with several code samples.
All code was written in Microsoft Visual Studio 2026.
Advantages: A Singleton provides controlled access to a shared resource, avoids the overhead and inconsistency of creating multiple instances, and can centralize application-wide services such as configuration, logging, or caching. When implemented carefully, it also supports lazy initialization, so the instance is created only when first requested.
Key Implementation Rules
To successfully build a singleton, you need to enforce two core rules:
- sealed class modifier: Prevents subclassing, which could otherwise result in multiple unique instances of the derived classes.
- private parameterless constructor: Prevents external code from creating new instances using the new keyword.
Main Source code Library Source code Test Source code
Example 1
Provides a list of month names and indices useful in a web project dropdown element for add and edit pages.
using SingletonsApp.Models;
using static System.Globalization.DateTimeFormatInfo;
namespace SingletonsApp.Classes.SingletonSamples;
public sealed class Basic1
{
private static readonly Lazy<Basic1> Lazy = new(() => new Basic1());
public static Basic1 Instance => Lazy.Value;
public List<MonthItem> MonthList =>
[
.. CurrentInfo.MonthNames[..^1]
.Select((monthName, index) =>
new MonthItem(index + 1, monthName))
];
}
💡 Using Lazy handles thread safety and lazy initialization automatically behind the scenes
ASP.NET Core example
Creates a dropdown using Helpers.Instance.MonthList same as above with a different class name.
@page
@model IndexModel
@{
ViewData["Title"] = "Month Selection";
}
<div class="container mt-5">
<div class="d-flex justify-content-center">
<form method="post">
<div class="mb-3">
<label asp-for="SelectedMonth" class="form-label">
Select month
</label>
<select asp-for="SelectedMonth"
class="form-select"
style="width: 250px;">
<option value="">Select a month</option>
@foreach (var month in Model.MonthItems)
{
<option value="@month.Index">
@month.Name
</option>
}
</select>
</div>
<button type="submit"
class="btn btn-primary">
Get Month
</button>
@if (Model.SelectedMonthItem is not null)
{
<div class="alert alert-success mt-3">
Selected month:
<strong>@Model.SelectedMonthItem.Name</strong>
(@Model.SelectedMonthItem.Index)
</div>
}
</form>
</div>
</div>
Backend
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using SingletonWebApp.Classes;
using SingletonWebApp.Models;
namespace SingletonWebApp.Pages;
public class IndexModel : PageModel
{
public List<MonthItem> MonthItems { get; set; } = [];
[BindProperty]
public int? SelectedMonth { get; set; }
public MonthItem? SelectedMonthItem { get; set; }
public void OnGet()
{
MonthItems = Helpers.Instance.MonthList;
}
public void OnPost()
{
MonthItems = Helpers.Instance.MonthList;
SelectedMonthItem = MonthItems
.FirstOrDefault(month => month.Index == SelectedMonth);
}
}
Do the same for Helpers.Instance.MonthList in other pages.
Dependency Injection version
using static System.Globalization.DateTimeFormatInfo;
using SingletonWebApp.Models;
namespace SingletonWebApp.Classes;
public sealed class Helpers
{
//private static readonly Lazy<Helpers> Lazy = new(() => new Helpers());
//public static Helpers Instance => Lazy.Value;
public List<MonthItem> MonthList { get; init; }
public Helpers()
{
MonthList = CurrentInfo.MonthNames[..^1]
.Select((monthName, index) =>
new MonthItem(index + 1, monthName)).ToList();
}
}
💡 Here we need a public constructor
Program.cs register Helpers class.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
// Register Helpers as a singleton
builder.Services.AddSingleton<Helpers>();
Index.cshtml.cs
💡 note use of primary constructor
public class IndexModel(Helpers helpers) : PageModel
{
public List<MonthItem> MonthItems { get; set; } = [];
[BindProperty]
public int? SelectedMonth { get; set; }
public MonthItem? SelectedMonthItem { get; set; }
private readonly Helpers _helpers = helpers;
public void OnGet()
{
MonthItems = _helpers.MonthList;
}
public void OnPost()
{
MonthItems = _helpers.MonthList;
SelectedMonthItem = MonthItems
.FirstOrDefault(month => month.Index == SelectedMonth);
}
}
Example 2
This example was originally done with a database and no singleton.
"TransactionInformation": {
"CurrentValue": "MW00001"
}
Each time a waiter takes a order for a table CurrentValue is incremented by 1 and updates the value in appsettings.json
public sealed class Basic2
{
private static readonly Lazy<Basic2> Lazy = new(() => new Basic2());
public static Basic2 Instance => Lazy.Value;
public TransactionInformation Transaction { get; set; }
public void UpdateTransactionIdentifier()
{
TransactionInformationReader.Save(Transaction);
}
private Basic2()
{
TransactionInformation? x = TransactionInformationReader.Load();
Transaction = new TransactionInformation()
{
CurrentValue = x.CurrentValue
};
}
}
Example 3
Uses IHostEnvironment Interface to determine if the current application is in development, staging or production environment and sets the application database connection.
using Microsoft.Extensions.Hosting;
using SingletonsApp.Models;
namespace SingletonsApp.Classes.Configuration;
public static class HostEnvironmentExtensions
{
extension(IHostEnvironment environment)
{
public bool CheckIsDevelopmentEnvironment() => environment.IsDevelopment();
public bool CheckIsStagingEnvironment() => environment.IsStaging();
public bool CheckIsProductionEnvironment() => environment.IsProduction();
public void DebugPrint(string databaseConnectionString, HelpDesk helpDesk)
{
if (environment.IsProduction()) return;
Console.WriteLine($" Current Environment: {environment.EnvironmentName}");
Console.WriteLine($" Application Name: {environment.ApplicationName}");
Console.WriteLine($" Content Root Path: {environment.ContentRootPath}");
Console.WriteLine($"Database Connection String: {databaseConnectionString}");
Console.WriteLine($" Help Desk Phone: {helpDesk.Phone}");
Console.WriteLine($" Help Desk Email: {helpDesk.Email}");
}
}
}
Singleton
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SingletonsApp.Classes.Configuration;
using SingletonsApp.Models;
namespace SingletonsApp.Singletons;
public sealed class EnvironmentSettings
{
private static readonly Lazy<EnvironmentSettings> Lazy = new(() => new EnvironmentSettings());
public static EnvironmentSettings Instance => Lazy.Value;
public AppEnvironment Environment { get; init; }
public HelpDesk HelpDesk { get; init; }
public string DatabaseConnectionString { get; init; } = null!;
private EnvironmentSettings()
{
DataConnections connections = AppSettingsReader.LoadConnectionStringsConnections();
HelpDesk = AppSettingsReader.LoadHelpDesk();
using IHost host = Host.CreateDefaultBuilder().Build();
IHostEnvironment environment = host.Services.GetRequiredService<IHostEnvironment>();
if (environment.CheckIsDevelopmentEnvironment())
{
Environment = AppEnvironment.Development;
DatabaseConnectionString = connections.ConnectionStrings.DevelopmentConnection;
}
if (environment.CheckIsStagingEnvironment())
{
Environment = AppEnvironment.Staging;
DatabaseConnectionString = connections.ConnectionStrings.StagingConnection;
}
if (environment.CheckIsProductionEnvironment())
{
Environment = AppEnvironment.Production;
DatabaseConnectionString = connections.ConnectionStrings.ProductionConnection;
}
}
}
Example 4
This example uses a singleton to get NuGet local packages and related information on a developer's computer.
Main Source code Frontend Source code
Required packages
- NuGet.Configuration
- NuGet.Protocol
using NuGet.Configuration;
using NuGetLibrary.Models;
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
namespace NuGetLibrary;
/// <summary>
/// Represents the settings for managing NuGet packages, including configuration
/// and the global packages folder path.
/// </summary>
public sealed class PackageSettings
{
private static readonly Lazy<PackageSettings> Lazy = new(() => new PackageSettings());
public static PackageSettings Instance => Lazy.Value;
public ISettings NuGetSettings { get; set; }
/// <summary>
/// Gets the path to the global NuGet packages folder.
/// </summary>
/// <value>
/// A string representing the path to the global NuGet packages folder.
/// </value>
public string Path { get; init; }
public List<string> DisabledSources { get; init; } = null!;
/// <summary>
/// Gets the list of NuGet packages
/// </summary>
public List<NuGetPackage> NuGetPackages { get; set; }
private PackageSettings()
{
NuGetSettings = Settings.LoadDefaultSettings(null);
Path = SettingsUtility.GetGlobalPackagesFolder(NuGetSettings);
NuGetPackages = PackageWork.Packages();
/*
* Get the list of disabled package sources from the NuGet configuration.
* The real purpose of this code is to show that from exploring the codebase, that there
* is a class NuGet.Configuration.ConfigurationConstants that contains constant node names
* to reference in the NuGet configuration file rather than hard coding them and by hard coding these
* may cause issues in the future if the node name changes.
*/
SettingSection? disableSources = NuGetSettings.GetSection(ConfigurationConstants.DisabledPackageSources);
if (disableSources is not null)
{
DisabledSources = disableSources!.Items.Select(x => x.GetAttributes().Values.FirstOrDefault()).ToList();
}
}
}


Top comments (1)