Static Members
Every field, property, or method in a C# class defaults to being an instance member: it lives on and operates against a particular object created with new. The static keyword changes that contract. A static member belongs to the type itself, not to any one instance, and there is exactly one copy of it shared across the entire application domain for the lifetime of the process. Static members are useful for state or behavior that is inherently global to a concept — a counter of how many objects have been created, a utility method that transforms input without needing object state, or constants that every instance should agree on. Because static members are not tied to an instance, you access them through the type name (MathHelpers.Square(4)), never through a variable holding an instance, and a static method cannot reference this or any non-static member without an explicit instance reference.
Cricket analogy: Each player has their own personal batting average that belongs to them, but the tournament's single shared points table belongs to the competition itself, accessed by tournament name rather than through any one player.
Static fields and static constructors
A static field is initialized once, the first time the type is used — either through a static member access or the creation of the first instance. If you need more control than field initializers give you, a static constructor (a parameterless constructor prefixed with static and no access modifier) lets you run setup logic exactly once. The CLR guarantees a static constructor runs before any static member is accessed and before any instance is created, and it runs at most once, in a thread-safe manner, even under heavy concurrent access.
Cricket analogy: A stadium's floodlight system is calibrated exactly once, the first time it's switched on before any match, guaranteed by ground staff to run before players even walk out, no matter how many matches follow.
public class ConnectionPool
{
private static readonly List<Connection> _pool;
private static int _totalCreated;
// Runs exactly once, lazily, before first use of ConnectionPool.
static ConnectionPool()
{
_pool = new List<Connection>(capacity: 10);
Console.WriteLine("ConnectionPool warmed up.");
}
public static Connection Rent()
{
if (_pool.Count == 0)
{
_totalCreated++;
return new Connection(id: _totalCreated);
}
var conn = _pool[^1];
_pool.RemoveAt(_pool.Count - 1);
return conn;
}
public static void Return(Connection conn) => _pool.Add(conn);
public static int TotalCreated => _totalCreated;
}
public record Connection(int Id);Static classes
Marking an entire class static (e.g. public static class MathHelpers) forbids instantiation entirely — the compiler generates no public constructor, and new MathHelpers() fails to compile. Every member of a static class must itself be static. This pattern is idiomatic for pure utility or extension-method containers such as System.Math or System.Linq.Enumerable, where the type exists only to group related stateless functions under a namespace-like umbrella.
Cricket analogy: Declaring an entire academy a 'coaching-only, non-playing' institution forbids it from fielding its own team; every coach on staff must purely teach, mirroring how a static class forbids instantiation and forces all-static members.
Unlike Java, where static members are inherited and can be shadowed but not overridden, C# treats static members similarly but disallows virtual, abstract, or override on them entirely — polymorphism only applies to instance members. If you need type-level polymorphic behavior, consider static abstract members in interfaces (C# 11+), which let generic code call a static method that differs per implementing type.
Static state is shared across every consumer of the type in the process, including across concurrent requests in a web server. Mutable static fields are a classic source of subtle race conditions and hidden coupling between unrelated parts of an application — treat static mutable state as you would a global variable, and prefer dependency injection with scoped/singleton lifetimes over ad hoc static caches.
Static local functions and static using directives
C# 8 introduced static local functions, which forbid capturing variables from the enclosing method — a useful guardrail that guarantees the local function cannot accidentally close over mutable outer state, and can slightly reduce allocation overhead. Separately, using static System.Math; imports the static members of a type directly into scope, letting you write Sqrt(2) instead of Math.Sqrt(2), which is handy for math-heavy or DSL-like code but should be used sparingly to avoid obscuring where a symbol comes from.
Cricket analogy: A commentator's booth mic is set to 'closed-circuit only,' forbidding it from picking up the crowd's live chatter (captured variables), while calling the ground simply 'the SCG' instead of its full sponsor name is a shorthand import.
- Static members belong to the type, not to any instance, and are shared across the whole process.
- Static constructors run at most once, lazily, before first use, and cannot take parameters.
- A static class cannot be instantiated and every member inside it must also be static.
- Static methods cannot access instance members or
thiswithout an explicit object reference. - Static abstract interface members (C# 11+) enable type-level polymorphism in generic code.
- Mutable static state is effectively global and should be used cautiously in concurrent applications.
Practice what you learned
1. What happens if you try to instantiate a class declared with the `static` modifier?
2. When does a static constructor run?
3. Which statement about static members and inheritance is correct in C#?
4. What is the primary risk of using mutable static fields in a web application?
5. What does the `static` modifier on a local function guarantee?
Was this page helpful?
You May Also Like
Encapsulation and Access Modifiers
Understand how C#'s access modifiers — public, private, protected, internal, and their combinations — let you control visibility and enforce encapsulation boundaries.
Classes and Objects
Understand how C# classes define blueprints for reference-type objects, covering fields, methods, object creation, and the distinction between a class and its instances.
Constructors and Initialization
Explore how constructors establish an object's initial state, including constructor overloading, chaining with 'this'/'base', field initializers, and object initializer syntax.
Dependency Injection Basics
Understand how dependency injection decouples classes from their collaborators, and how the built-in .NET DI container manages object lifetimes.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics