Glossary / Static constructors

Static constructors

A static constructor initializes static state for a type. It runs at most once per type initialization context, before the type is first used in certain ways; developers cannot call it directly or control its exact timing beyond language/runtime guarantees.

Initialization behavior

A static constructor has no access modifier or parameters. Static field initializers run before its body. If initialization throws, the type is generally left uninitialized and later use can fail with TypeInitializationException wrapping the original failure.

Types without an explicit static constructor may be marked beforefieldinit, allowing the runtime more freedom to run field initializers before the first explicit static member access. Adding a static constructor can change timing guarantees and performance. Generic types have static state per closed constructed type, so Cache<int> and Cache<string> initialize separately.

Engineering guidance

Keep static initialization cheap, deterministic, and unlikely to fail. Avoid I/O, blocking, and complex dependency resolution; use explicit async initialization or lazy factories when necessary. Initialization locking can interact with other locks and create deadlocks.

Interview questions

  1. Can code call a static constructor? No; the runtime invokes it.
  2. How many times does it run? At most once per type initialization context/closed generic type.
  3. What if it throws? Type initialization fails and later uses commonly surface TypeInitializationException.
  4. What is beforefieldinit? Metadata that gives the runtime flexibility to initialize earlier than first explicit static access.
  5. Why avoid async work in one? Constructors cannot be async and blocking initialization harms startup; use explicit async initialization.

References