There’s a common misconception — and it often shows up as an interview question:
On the first access to a type, the very first thing that runs is the type’s static constructor.
But if a static field is initialized with an instance of the same type, the order flips: the instance constructor can run before the static constructor. This is not a bug — it’s defined by the CLI/ECMA specification.
Minimal example
class MyLogger
{
static MyLogger inner = new MyLogger();
static MyLogger()
{
Console.WriteLine("Static");
}
private MyLogger()
{
Console.WriteLine("Instance");
}
}
The expectation is often:
StaticInstance
Actual output:
Instance
Static
What the specification actually guarantees
It’s important to separate two concepts:
- static field initializers (field initializers),
- the body of the static constructor (
static MyLogger() { ... }).
During type initialization, code runs in a fixed order:
- first — static field initializers (in declaration order),
- then — the body of the
staticconstructor.
So if a static field initializer calls new MyLogger(), it triggers the instance constructor before execution reaches Console.WriteLine("Static").
Why this can become a bug
The problem appears when the instance constructor (or anything it calls) assumes the static part of the type is already “up”. But it isn’t — execution is still inside static field initialization.
A real crash example:
class Service
{
static string ConnectionString;
static Service Instance = new Service();
static Service()
{
ConnectionString = LoadFromConfig();
}
private Service()
{
// ConnectionString is still null
Console.WriteLine(ConnectionString.Length);
}
}
Here, Instance = new Service() runs before ConnectionString is assigned, so accessing Length will throw a NullReferenceException.
How to fix it (and how not to write it)
1) Don’t create instances in static field initializers when there are dependencies
The simplest way to control the order is to move the creation into the static constructor after setting up the required state:
class MyLogger
{
static MyLogger inner;
static MyLogger()
{
// first: any static setup
inner = new MyLogger();
Console.WriteLine("Static");
}
private MyLogger()
{
Console.WriteLine("Instance");
}
}
2) Use Lazy<T> for lazy and thread-safe initialization
static readonly Lazy<MyLogger> inner = new(() => new MyLogger());
static MyLogger Inner => inner.Value;
This makes the creation moment explicit: the object is created on the first access to Inner, not during type initialization.
3) Minimize side effects in constructors
If a constructor pulls config, logging, service locator, DI, etc., the risk of “early” access to uninitialized statics is higher.
Takeaway
“Static constructors always run first” is too rough — and misleading in this case.
The correct model is:
- on first use of a type, type initialization happens;
- inside it, static field initializers run first (in declaration order);
- and only after that does the body of the
staticconstructor run.
That’s why static Foo x = new Foo() can trigger the instance constructor before any code inside static Foo() executes.
Top comments (0)