Advertisement
Intermediate C# Lesson 9 of 10

Lesson 9: Static Members & Singleton Pattern in C#

Static members belong to the class, not to instances. Static constructors run once per class to initialize static state. The Singleton pattern ensures a class has exactly one instance, providing a global point of access to a shared resource.

Advertisement

Static Fields & Methods

Static fields and methods are shared across all instances of a class. They are accessed via the class name, not an instance.

public class BankAccount
{
    private static decimal _totalDeposits = 0;
    private decimal _balance = 0;

    public static decimal TotalDeposits
    {
        get { return _totalDeposits; }
    }

    public void Deposit(decimal amount)
    {
        _balance += amount;
        _totalDeposits += amount;
    }
}

// Usage
BankAccount acc1 = new BankAccount();
BankAccount acc2 = new BankAccount();

acc1.Deposit(1000);
acc2.Deposit(500);

Console.WriteLine(BankAccount.TotalDeposits); // 1500

Static Constructors

A static constructor runs once before any instance is created. It initializes static fields.

public class Configuration
{
    public static string ConnectionString { get; private set; }
    public static int MaxConnections { get; private set; }

    // Static constructor — runs once per class
    static Configuration()
    {
        ConnectionString = "Server=localhost;Database=MyApp";
        MaxConnections = 100;
        Console.WriteLine("Configuration initialized");
    }

    // Instance constructor
    public Configuration()
    {
        Console.WriteLine("Instance created");
    }
}

// Usage
var cfg1 = new Configuration();  // Output: Configuration initialized, Instance created
var cfg2 = new Configuration();  // Output: Instance created (static ran once)

The Singleton Pattern

A Singleton ensures only one instance of a class ever exists. The instance is created lazily on first access and cached for reuse.

public class Logger
{
    private static Logger _instance;

    private Logger()
    {
        // Private constructor prevents outside instantiation
    }

    public static Logger GetInstance()
    {
        if (_instance == null)
        {
            _instance = new Logger();
        }
        return _instance;
    }

    public void Log(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }
}

// Usage
Logger log1 = Logger.GetInstance();
Logger log2 = Logger.GetInstance();

Console.WriteLine(ReferenceEquals(log1, log2)); // true

Thread-Safe Singleton with Lazy<T>

For thread-safe lazy initialization, use Lazy<T> to avoid double-locking issues.

public class DatabaseConnection
{
    private static readonly Lazy<DatabaseConnection> _instance =
        new Lazy<DatabaseConnection>(() => new DatabaseConnection());

    public static DatabaseConnection Instance
    {
        get { return _instance.Value; }
    }

    private DatabaseConnection()
    {
        Console.WriteLine("Database connection created");
    }

    public void Query(string sql)
    {
        Console.WriteLine($"Executing: {sql}");
    }
}

// Usage
var db1 = DatabaseConnection.Instance;
var db2 = DatabaseConnection.Instance;

Console.WriteLine(ReferenceEquals(db1, db2)); // true
Advertisement

🧠 Quick Check — Lesson 9

What is the primary purpose of the Singleton pattern?

When to Use Static Members

Use static members for:

  • Shared counters (e.g., total objects created)
  • Configuration constants
  • Utility methods that don't need instance state
  • Factory methods

Singleton Use Cases

Singletons are appropriate for:

  • Database connections (manage one active connection pool)
  • Loggers (centralize logging)
  • Configuration managers (one global config)
  • Thread pools and thread-safe resource managers

Lesson Summary

Static members belong to the class, not instances, and are shared across all objects.

Static constructors run once per class to initialize shared state.

The Singleton pattern guarantees one instance and provides a global access point.

Use Lazy<T> for thread-safe Singleton initialization in production code.

Up Next

Lesson 10: Real-World OOP Project

Final Lesson →