This code seems to not call the Mixed
constructor and prints y = 0
public class Mixed
{
public int x;
public static int y;
public Mixed()
{
x = 1;
y = 1;
}
}
public class Program
{
static Mixed mixed = new Mixed();
static void Main(string[] args)
{
Console.WriteLine("y = " + Mixed.y);
Console.ReadLine();
}
}
However, simply modifying the Main
function to look like this results in the constructor being called.
static void Main(string[] args)
{
Console.WriteLine("x = " + mixed.x);
Console.WriteLine("y = " + Mixed.y);
Console.ReadLine();
}
This prints:
x = 1
y = 1
Why does simply adding this reference to a non-static field result in the constructor being called correctly? Shouldn't creating the object always result in a constructor being called, regardless of how that object is used later in the program?
Oddly enough, making the Mixed
object non-static like this also results in the constructor being called:
public class Program
{
static void Main(string[] args)
{
Mixed mixed = new Mixed();
Console.WriteLine("y = " + Mixed.y);
Console.ReadLine();
}
}
However, this doesn't seem to make sense to me either. Declaring the Mixed
object as static should only imply that there is only one copy of the object in memory, regardless of how many times Program is instantiated. Is this some sort of compiler optimization that for static fields the compiler waits for a reference to a non-static field of that type before actually instantiating it?
What you're experiencing is that the static field of the Program
type are not being initialized.
You will find that all static fields of a type are initialized when any of them are accessed for the first time. This is agreed between the different runtimes (.NET Framework, .NET Core, Mono).
As seen in the following IL code, your example will generate an .cctor
(static constructor) which initializes the fields.
.class public auto ansi beforefieldinit ConsoleApp1.Program
extends [System.Runtime]System.Object
{
.field private static class ConsoleApp1.Mixed mixed
// (...) omitted irrelevant methods
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: newobj instance void ConsoleApp1.Mixed::.ctor()
IL_0005: stsfld class ConsoleApp1.Mixed ConsoleApp1.Program::mixed
IL_000a: ret
}
}
It is however not agreed at what point the fields will be initialized. .NET Core for instance will lazy load the fields, just before any of them are being accessed. .NET Framework will eagerly load the fields when any member of the type (including methods) are being accessed.
The following code will have different results in .NET Core and .NET Framework.
public class Program
{
static Mixed mixed = new Mixed();
static string text = "Hello, World!";
static void Main(string[] args)
{
Console.WriteLine("y = " + Mixed.y);
Console.WriteLine(text);
Console.WriteLine("y = " + Mixed.y);
Console.ReadLine();
}
}
In .NET Core:
y = 0
Hello, World!
y = 1
In .NET Framework:
y = 1
Hello, World!
y = 1
If you add a static constructor to the class, the fields will always be initialized when any field or method is accessed:
// ... adding to previous Program class
static Program()
{
// empty body
}
In .NET Core:
y = 1
Hello, World!
y = 1
This happens because the beforefieldinit
flag will not be added to a type with a custom static constructor. This flag indicates that the fields of the type can be loaded as late/lazy as possible.
When this flag is omitted, the fields will be initialized eagerly. When the flag is present, the fields are at least loaded lazily but could also be loaded eagerly, this is up to the runtime.
As seen previously, .NET Core will load the fields lazily when the flag is present. .NET Framework will will load the fields eagerly, no matter if the flag is present.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With