Knara.SourceGenerators.DesignPatterns.Singleton
1.0.2
See the version list below for details.
dotnet add package Knara.SourceGenerators.DesignPatterns.Singleton --version 1.0.2
NuGet\Install-Package Knara.SourceGenerators.DesignPatterns.Singleton -Version 1.0.2
<PackageReference Include="Knara.SourceGenerators.DesignPatterns.Singleton" Version="1.0.2" />
<PackageVersion Include="Knara.SourceGenerators.DesignPatterns.Singleton" Version="1.0.2" />
<PackageReference Include="Knara.SourceGenerators.DesignPatterns.Singleton" />
paket add Knara.SourceGenerators.DesignPatterns.Singleton --version 1.0.2
#r "nuget: Knara.SourceGenerators.DesignPatterns.Singleton, 1.0.2"
#:package Knara.SourceGenerators.DesignPatterns.Singleton@1.0.2
#addin nuget:?package=Knara.SourceGenerators.DesignPatterns.Singleton&version=1.0.2
#tool nuget:?package=Knara.SourceGenerators.DesignPatterns.Singleton&version=1.0.2
Singleton Pattern Generator
A C# source generator that creates thread-safe singleton implementations for .NET Framework applications. Automatically generates correct singleton code and validates thread safety to prevent common concurrency bugs.
Why This Generator Exists
Problem: Manual singleton implementations are error-prone and often contain race conditions that cause intermittent bugs in production. Without dependency injection containers (available in .NET Framework 4.x), singletons are essential for managing shared state.
Solution: Generate proven, thread-safe singleton implementations automatically with built-in validation for common concurrency issues.
Quick Start
- Add the source generator to your project:
<ItemGroup> <ProjectReference Include="path/to/Knara.SourceGenerators.DesignPatterns.Singleton.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
Or via NuGet (when published):
dotnet add package Knara.SourceGenerators.DesignPatterns.Singleton
If you are using the generator in .net 4.+ projects, refer to this guide for additional steps.
- Add the
[Singleton]attribute to your partial class - Make sure your class has a private constructor
- The generator creates the singleton implementation automatically
using Knara.SourceGenerators.DesignPatterns.Singleton;
[Singleton(Strategy = SingletonStrategy.Lazy)]
public partial class ConfigurationManager
{
private ConfigurationManager() { } // Private constructor required
public string GetSetting(string key) { /* your code */ }
}
// Usage
var config = ConfigurationManager.Instance;
string setting = config.GetSetting("DatabaseTimeout");
Singleton Strategies
Choose the right strategy based on your use case:
1. Lazy (Default) - SingletonStrategy.Lazy
When to use: Most general-purpose scenarios Performance: Good initialization, good access speed Memory: Minimal overhead
[Singleton(Strategy = SingletonStrategy.Lazy)]
public partial class ConfigurationManager
{
private readonly ConcurrentDictionary<string, string> _settings = new();
private ConfigurationManager() { }
}
Best for:
- Configuration managers
- Service registries
- Non-performance-critical singletons
2. Eager - SingletonStrategy.Eager
When to use: When you need the instance ready immediately at startup Performance: Fastest access (no initialization checks) Memory: Instance created at application start
[Singleton(Strategy = SingletonStrategy.Eager)]
public partial class Logger
{
private readonly string _logFilePath;
private Logger()
{
_logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app.log");
}
}
Best for:
- Logging systems
- Critical infrastructure components
- Components needed immediately at startup
3. LockFree - SingletonStrategy.LockFree
When to use: High-performance scenarios with frequent access Performance: Fastest for read-heavy workloads Memory: Minimal locking overhead
[Singleton(Strategy = SingletonStrategy.LockFree)]
public partial class MetricsCollector
{
private readonly ConcurrentDictionary<string, long> _counters = new();
private MetricsCollector() { }
}
Best for:
- Metrics collection
- Caching systems
- High-frequency data access
4. DoubleCheckedLocking - SingletonStrategy.DoubleCheckedLocking
When to use: When you need precise control over initialization timing Performance: Good balance of speed and memory efficiency Memory: Standard locking overhead
[Singleton(Strategy = SingletonStrategy.DoubleCheckedLocking)]
public partial class DbConnectionPool
{
private readonly ConcurrentQueue<IDbConnection> _connections = new();
private DbConnectionPool() { }
}
Best for:
- Database connection pools
- Resource managers
- Components with expensive initialization
Thread Safety Validation
The generator automatically checks for common thread safety issues:
❌ Dangerous Collections
[Singleton]
public partial class BadExample
{
private Dictionary<string, int> _data = new(); // ❌ Will generate warning
private List<string> _items = new(); // ❌ Will generate warning
}
✅ Thread-Safe Alternatives
[Singleton]
public partial class GoodExample
{
private ConcurrentDictionary<string, int> _data = new(); // ✅ Thread-safe
private ConcurrentBag<string> _items = new(); // ✅ Thread-safe
}
Generic Singletons
The generator supports generic singletons with constraints:
[Singleton(Strategy = SingletonStrategy.DoubleCheckedLocking)]
public partial class Repository<T> where T : IEntity
{
private readonly ConcurrentBag<T> _items = new();
private Repository() { }
public void Add(T item) => _items.Add(item);
public IReadOnlyList<T> GetAll() => _items.ToList().AsReadOnly();
}
// Usage
var userRepo = Repository<User>.Instance;
var productRepo = Repository<Product>.Instance;
Initialization Support
Add an Initialize() method for post-construction setup:
[Singleton]
public partial class CacheManager
{
private CacheManager() { }
private void Initialize()
{
// Called automatically after instance creation
_ = Task.Run(CleanupExpiredEntries);
Console.WriteLine("Cache cleanup task started");
}
}
Requirements
- Partial class: Your class must be declared as
partial - Private constructor: Required to prevent external instantiation
- .NET Framework 4.0+: Compatible with legacy applications
Common Patterns
Configuration Management
[Singleton(Strategy = SingletonStrategy.Lazy)]
public partial class AppConfig
{
private readonly ConcurrentDictionary<string, string> _settings = new();
private AppConfig() { LoadFromFile(); }
}
Resource Pooling
[Singleton(Strategy = SingletonStrategy.LockFree)]
public partial class ObjectPool<T>
{
private readonly ConcurrentQueue<T> _objects = new();
private ObjectPool() { PrePopulatePool(); }
}
Metrics Collection
[Singleton(Strategy = SingletonStrategy.LockFree)]
public partial class PerformanceCounters
{
private readonly ConcurrentDictionary<string, long> _counters = new();
private PerformanceCounters() { }
}
Warnings and Diagnostics
The generator provides helpful warnings:
| Warning | Meaning | Solution |
|---|---|---|
| Non-thread-safe field | Using Dictionary, List, etc. |
Use ConcurrentDictionary, ConcurrentBag, etc. |
| Public constructor | Constructor is publicly accessible | Make constructor private |
| Class not partial | Cannot generate singleton implementation | Add partial keyword |
Performance Comparison
| Strategy | Initialization | Access Speed | Memory | Use Case |
|---|---|---|---|---|
| Lazy | Lazy | Good | Low | General purpose |
| Eager | Immediate | Fastest | Higher | Critical systems |
| LockFree | Lazy | Fastest | Low | High-frequency access |
| DoubleCheckedLocking | Lazy | Good | Low | Balanced performance |
Best Practices
✅ Do
- Use
ConcurrentDictionaryinstead ofDictionary - Use
ConcurrentBaginstead ofList - Make constructors private
- Use
Initialize()method for setup logic - Choose appropriate strategy for your use case
❌ Don't
- Use non-thread-safe collections as instance fields
- Make constructors public
- Forget the
partialkeyword - Use singletons for everything (only when truly needed)
Migration from Manual Singletons
Before (error-prone):
public class MyService
{
private static MyService _instance;
private static readonly object _lock = new object();
public static MyService Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new MyService();
}
}
return _instance;
}
}
}
After (generated, correct):
[Singleton]
public partial class MyService
{
private MyService() { }
// All singleton logic generated automatically
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.14.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.