Previous Agile Estimation Deployment-Strategies Next

Singleton Pattern in C#

The Singleton Pattern ensures a class has only one instance and provides a global point of access to it.

Key Characteristics

  • Private constructor
  • Static instance
  • Public static accessor
  • Thread safety

Implementations

1. Non-Thread-Safe Singleton (Not Recommended)

public sealed class Singleton {
    private static Singleton instance = null;
    private Singleton() { }

    public static Singleton Instance {
        get {
            if (instance == null)
                instance = new Singleton();
            return instance;
        }
    }
}

2. Thread-Safe with Locking

public sealed class Singleton {
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    private Singleton() { }

    public static Singleton Instance {
        get {
            lock (padlock) {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }
    }
}

3. Double-Checked Locking (Risky)

public sealed class Singleton {
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    private Singleton() { }

    public static Singleton Instance {
        get {
            if (instance == null) {
                lock (padlock) {
                    if (instance == null)
                        instance = new Singleton();
                }
            }
            return instance;
        }
    }
}

4. Eager Initialization

public sealed class Singleton {
    private static readonly Singleton instance = new Singleton();

    static Singleton() { }

    private Singleton() { }

    public static Singleton Instance => instance;
}

5. Lazy Initialization via Nested Class

public sealed class Singleton {
    private Singleton() { }

    public static Singleton Instance => Nested.instance;

    private class Nested {
        static Nested() { }
        internal static readonly Singleton instance = new Singleton();
    }
}

6. Using .NET's Lazy<T>

public sealed class Singleton {
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance => lazy.Value;

    private Singleton() { }
}

Best Practices

  • Use sealed to prevent inheritance
  • Prefer Lazy<T> for simplicity and thread safety
  • Avoid shared mutable state unless synchronized
  • Document intent clearly

Use Cases

  • Logging
  • Configuration
  • Caching
  • Thread pools

Disadvantages of Singleton Pattern

The Singleton Pattern can lead to global state, reduced testability, tight coupling, hidden dependencies, and potential concurrency issues. These challenges can make code harder to understand, maintain, and debug.

1. Global State

  • Difficulty in Managing: Changes to the singleton's state affect the entire application, making it harder to manage and debug.
  • Unintended Consequences: Modifications can have unforeseen effects across different parts of the codebase.

2. Reduced Testability

  • Tight Coupling: Code becomes tightly coupled to the singleton, complicating mocking and replacement during testing.
  • Challenges with Mocking: Substituting singletons for tests can be complex and unreliable.

3. Tight Coupling

  • Global Accessibility: Singleton access from anywhere increases coupling between classes.
  • Refactoring Difficulties: Changes to the singleton can ripple through the codebase, making refactoring harder.

4. Hidden Dependencies

  • Obscure Dependencies: Classes access singletons directly, hiding dependencies that should be explicit.
  • Maintainability Challenges: Hidden dependencies reduce code clarity and maintainability.

5. Concurrency Issues

  • Race Conditions: Simultaneous access in multi-threaded environments can cause data inconsistency.

6. Single Responsibility Principle Violation

  • Overloaded Responsibilities: Singletons may take on unrelated tasks, violating SRP.

7. Lack of Flexibility

  • Difficult to Replace: Switching to a different implementation often requires modifying the singleton itself.
  • Limited Runtime Changes: Altering behavior at runtime is difficult.

8. Potential Memory Management Issues

  • Persistence: Singleton instances live for the application's lifetime, potentially consuming more memory.
  • Memory Leaks: Improper management can lead to retained resources and leaks.
Back to Index
Previous Agile Estimation Deployment-Strategies Next
*