← Open in the full interactive course (progress tracking, search & more)

Static members belong to the type itself, not to any instance.

Imagine you're building a system that tracks how many users are currently logged in. You need a single counter that every part of your application can access and update — but you don't want to create an object just to hold that counter.

That's where static members come in. They belong to the class itself, not to any particular object. Static fields hold data that is shared across all instances, and static methods provide utility functions that don't depend on object state.

In this lesson, you'll learn about static fields, properties, methods, constructors, and classes — and when to use them in your applications.

What Is It?

The Simple Explanation

A static member is a field, property, method, or constructor that belongs to the class itself, not to any specific object. You access it using the class name, not an instance variable.

Static = Type-level, Instance = Object-level

Instance members — each object has its own copy (e.g., person.Name).

Static members — shared across all objects (e.g., Person.TotalCount).

Static members exist even if no objects of the class have been created.

The Technical Definition

A static member is declared with the static keyword. It is associated with the type, not with instances. Static members are stored in a special area of memory that is shared across all instances and accessed via the type name.

Member Type Keyword Access Example
Instance field obj.Name Each object has its own
Static field static ClassName.Value Shared across all objects
Static method static ClassName.Method() Utility / helper functions
Static constructor static Runs once per type
Static class static class ClassName.Method() Contains only static members

Why Does It Exist?

The Problem

Sometimes you need data or functionality that isn't tied to a specific object. For example:

Without static members, you'd have to create a dummy object just to hold such data or methods — which is awkward and inefficient.

The Solution

Static members provide a clean way to:

Big Picture

Here's how static members relate to the type and its instances:

STATIC VS INSTANCE — MEMORY & ACCESS
STATIC (Type-level)
Stored in a single location
Shared by all objects
Exists even with zero instances
ClassName.Member
INSTANCE (Object-level)
Stored per object
Unique to each instance
Exists only after creation
obj.Member
Static field
shared value
Static method
utility / factory
Static constructor
one-time setup

How It Works

Let's explore different types of static members with examples.

STATIC MEMBERS — STEP BY STEP
1. Static field — shared data
public class Counter { public static int TotalInstances = 0; // static field public int InstanceNumber { get; } public Counter() { TotalInstances++; InstanceNumber = TotalInstances; } } var c1 = new Counter(); var c2 = new Counter(); var c3 = new Counter(); Console.WriteLine(Counter.TotalInstances); // 3 Console.WriteLine(c1.InstanceNumber); // 1 Console.WriteLine(c2.InstanceNumber); // 2 Console.WriteLine(c3.InstanceNumber); // 3

TotalInstances is shared across all objects. Each constructor increments it.

2. Static property — controlled access to static field
public class Configuration { private static string _environment = "Development"; public static string Environment { get => _environment; set { if (value == "Production" || value == "Development" || value == "Staging") _environment = value; else throw new ArgumentException("Invalid environment."); } } } Console.WriteLine(Configuration.Environment); // Development Configuration.Environment = "Production"; Console.WriteLine(Configuration.Environment); // Production

Static properties allow validation and logic when accessing static data.

3. Static method — utility / factory
public class MathHelper { // Static utility method public static int Add(int a, int b) => a + b; // Static factory method public static Counter CreateCounter() { return new Counter(); } } int sum = MathHelper.Add(5, 3); // 8 var counter = MathHelper.CreateCounter();

Static methods are called on the class itself. They don't have a this reference.

4. Static constructor — one-time initialisation
public class Database { private static string ConnectionString; static Database() { // Runs once when the type is first accessed ConnectionString = LoadFromConfig(); Console.WriteLine("Static constructor ran."); } private static string LoadFromConfig() => "Server=localhost;Db=MyDb;"; public static void Connect() { Console.WriteLine($"Connecting with {ConnectionString}"); } } Database.Connect(); // Static constructor runs first

Static constructors are perfect for loading configuration, initialising static fields, etc.

5. Static class — contains only static members
public static class StringExtensions { public static string Reverse(this string input) // extension method { return new string(input.Reverse().ToArray()); } public static bool IsNullOrWhitespace(string input) { return string.IsNullOrWhiteSpace(input); } } string reversed = "hello".Reverse(); // "olleh" (using extension method) bool empty = StringExtensions.IsNullOrWhitespace(" "); // true

Static classes cannot be instantiated or inherited. They're ideal for utility libraries.

Simple Example

Let's build a Library system that tracks total books and provides utility methods.

public class Book { // ─── Static members ─── private static int _nextId = 100; private static int _totalBooks = 0; public static int TotalBooks => _totalBooks; // Static constructor: runs once static Book() { Console.WriteLine("Book static constructor: Initialising."); } // Static factory method public static Book CreateBook(string title, string author) { return new Book(title, author); } // ─── Instance members ─── public int Id { get; } public string Title { get; } public string Author { get; } private Book(string title, string author) { if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title required."); if (string.IsNullOrWhiteSpace(author)) throw new ArgumentException("Author required."); Id = Interlocked.Increment(ref _nextId); Title = title; Author = author; _totalBooks++; } public void Display() { Console.WriteLine($"#{Id}: {Title} by {Author}"); } } // ─── Static class for utilities ─── public static class BookUtilities { public static string FormatTitle(string title) { return title.ToUpperInvariant(); } public static bool IsValidIsbn(string isbn) { return isbn?.Length == 13 && isbn.All(char.IsDigit); } } // ─── Usage ─── var book1 = Book.CreateBook("1984", "George Orwell"); var book2 = Book.CreateBook("Brave New World", "Aldous Huxley"); book1.Display(); // #101: 1984 by George Orwell book2.Display(); // #102: Brave New World by Aldous Huxley Console.WriteLine($"Total books: {Book.TotalBooks}"); // 2 Console.WriteLine(BookUtilities.FormatTitle("hello")); // HELLO Console.WriteLine(BookUtilities.IsValidIsbn("9781234567890")); // True

Key takeaways from this example:

Real-World Example

In a typical application, static members are used for logging, configuration, and the singleton pattern.

// ─── Singleton using static members ─── public class AppConfig { private static AppConfig? _instance; private static readonly object _lock = new(); private AppConfig() { } // Private constructor prevents external instantiation public static AppConfig Instance { get { if (_instance == null) { lock (_lock) { _instance ??= new AppConfig(); } } return _instance; } } public string Environment { get; set; } = "Development"; public int MaxRetries { get; set; } = 3; } // ─── Static logger ─── public static class Logger { private static readonly string _logFilePath; static Logger() { _logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "app.log"); Console.WriteLine($"Logger initialised. Log file: {_logFilePath}"); } public static void LogInfo(string message) { var entry = $"[INFO] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}"; Console.WriteLine(entry); // In a real app, you'd write to a file. } public static void LogError(string message, Exception? ex = null) { var entry = $"[ERROR] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}" + (ex != null ? $"\n{ex}" : ""); Console.WriteLine(entry); } } // ─── Usage in a real application ─── var config = AppConfig.Instance; Logger.LogInfo($"Application starting in {config.Environment} mode."); Logger.LogInfo($"Max retries: {config.MaxRetries}"); try { // Simulate some work throw new InvalidOperationException("Something went wrong."); } catch (Exception ex) { Logger.LogError("An error occurred.", ex); }

This example shows:

Analogy

Static = Company, Instance = Employee

Think of a class as a company. The company itself has attributes that are shared by all employees: the company name, the address, the number of employees.

Each employee (object) has their own attributes: name, salary, employee ID.

Static members are like the company's central resources; instance members are like each employee's personal belongings.

Under the Hood

How does the .NET runtime handle static members?

STATIC MEMBERS — INTERNAL VIEW
1. Storage
2. Type Initialisation
3. Method Calls
4. Static Classes

Common Confusion

1. Static vs Instance — When to Use Which

Use instance members when the data or behaviour depends on the specific object's state. Use static members when the data or behaviour is common to all objects or doesn't require any object state.

2. Static Methods Can't Access Instance Members

A static method cannot directly access instance fields or methods because it doesn't have a this reference. If you need to access instance data, you must pass an instance reference as a parameter.

public class Example { private int _value; public static void StaticMethod(Example obj) // pass instance { Console.WriteLine(obj._value); } }

3. Static Constructor vs Instance Constructor

An instance constructor runs each time an object is created. A static constructor runs once, before any static member is accessed or any instance is created. Static constructors cannot be called explicitly and have no parameters.

Common Mistakes

Mistake 1 — Using static for state that should be per-instance

Wrong:

public class User { public static string Name { get; set; } // All users share the same name — bad! }

Correct:

public class User { public string Name { get; set; } // Each user has their own name }

Mistake 2 — Static methods that depend on instance data

Wrong:

public static void PrintName() { Console.WriteLine(Name); // Error: Can't access instance field Name }

Correct (pass an instance):

public static void PrintName(User user) { Console.WriteLine(user.Name); }

Mistake 3 — Not handling thread safety for static fields

If you have static fields accessed from multiple threads, you must synchronise access (e.g., using locks).

private static int _counter; public static void Increment() { Interlocked.Increment(ref _counter); // Thread-safe increment }

Mistake 4 — Overusing static classes instead of dependency injection

Static classes make code harder to test and can create hidden dependencies. In modern applications, prefer dependency injection over static utilities for shared services.

When Should I Use It?

Use static members when:
Avoid static members when:

Mental Model

Static = belongs to the class, shared, accessible without an object
Instance = belongs to each object, separate, accessed via object
Static field = one copy for the whole type
Static method = utility or factory, no this
Static constructor = one-time type initialisation
Static class = pure utilities, cannot be instantiated

Remember:
· Access static members via ClassName.Member
· Static methods cannot access instance members directly
· Static constructors run automatically and cannot be called directly
· Use static sparingly; prefer instance-based design with dependency injection for testability

Key Takeaway


Check Your Understanding

You've seen how static members work. Let's test your knowledge.

1. Which of the following statements about static members is true?

Show answer

Correct: C

Why C is correct: Static members belong to the class itself, not to any instance. They are shared across all objects and exist even when no objects are created.

Why A is incorrect: Static members are accessed using the class name, not an instance.

Why B is incorrect: Static members are not per-object; they are per-type.

Why D is incorrect: Static members exist even without any instances. They can be accessed at any time.

Reinforcement: Static = type-level, shared; instance = object-level, separate.

2. Can a static method access an instance field directly?

Show answer

Correct: C

Why C is correct: Static methods do not have a this reference, so they cannot access instance members directly. They would need an instance reference passed as a parameter.

Why A is incorrect: Static methods cannot access instance fields directly.

Why B is incorrect: Even if the field is public, the static method still lacks a specific instance to access it.

Why D is incorrect: Being in the same class doesn't give the static method a this reference.

Reinforcement: Static methods operate at the type level; instance members require an object.

3. When does a static constructor run?

Show answer

Correct: B

Why B is correct: A static constructor runs automatically once, before any static member is accessed or any instance of the type is created. It is called by the runtime when the type is first referenced.

Why A is incorrect: Static constructors run once, not per instance.

Why C is incorrect: They run at initialisation, not at termination.

Why D is incorrect: You cannot call a static constructor explicitly; it's invoked by the runtime.

Reinforcement: Static constructors are perfect for one-time setup, like loading configuration.

4. Which of the following correctly declares a static class?

Show answer

Correct: A

Why A is correct: A static class is declared with the static keyword. It cannot be instantiated and can only contain static members.

Why B is incorrect: This is a regular class with a static method, not a static class.

Why C is incorrect: sealed prevents inheritance but doesn't make it static.

Why D is incorrect: abstract classes can have instance members and can be inherited.

Reinforcement: Static classes are containers for utility methods and constants.

5. What is the output of the following code?

public class Test { public static int Count = 0; public int InstanceNumber; public Test() { Count++; InstanceNumber = Count; } public static void ResetCount() => Count = 0; } var t1 = new Test(); var t2 = new Test(); Test.ResetCount(); var t3 = new Test(); Console.WriteLine($"{t1.InstanceNumber} {t2.InstanceNumber} {t3.InstanceNumber}");
Show answer

Correct: A — 1 2 1

Why A is correct: Count is static and shared. After two objects, Count = 2. ResetCount() sets Count = 0. Then t3 is created, incrementing Count to 1, so t3.InstanceNumber = 1. t1 and t2 have their numbers already set (1 and 2). So output is "1 2 1".

Why B is incorrect: This would happen if ResetCount wasn't called.

Why C is incorrect: This would be if the reset didn't happen and counter started at a different base.

Why D is incorrect: This would be if instance numbers were not set correctly.

Reinforcement: Static fields are shared; changes affect all objects. Instance fields capture the value at creation time.

You now understand static members — the type-level features that provide shared state and utility in C#!


dotnetmadeeasy.com — Learn C# and .NET, the right way.