I was wondering what gets initialized first in following example
A.a
or B.b
class A {
static int a = methodA();
static int methodA() { return 5; }
}
class B {
static int b = methodB();
static int methodB() { return A.a; } // here is the problem: `b` depends on `a`
}
If a
, then all is fine
methodA
returns 5, which is stored to a
. Then methodB
retrieves the value of a
and returns it.
But if b
is first, it gets garbage value from a
, and, after that, a
is initialized to 5.
CodePudding user response:
You are right, it is a race condition in the language specification itself. Either one will be run first, and you have no control over it. You either pray the specific compilation that linked your code, as well as the JIT that's running your code got it "right" (from your point of view), or you simply write correct, deterministic code.
One option to get rid of this is to use one static constructor to initialize the dependent static variables sequentially.
it gets garbage value from
a
That will never happen by the way, the field will be either zero-initialized or initialized by your code.
CodePudding user response:
In looking at the c# language specification, I don't see how there is any race condition. This is a quote from the section on static constructors:
To initialize a new closed class type, first a new set of static fields (Static and instance fields) for that particular closed type is created. Each of the static fields is initialized to its default value (Default values). Next, the static field initializers (Static field initialization) are executed for those static fields. Finally, the static constructor is executed.
What is happening in the code example above is "static field initialization." The spec lists that as happening immediately after default value initialization, and before any static constructor is called.
Take the following code as an example:
using System;
namespace console1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("begin main()");
var x = B.b;
Console.WriteLine($"x = {x}");
}
}
public class B
{
static B()
{
Console.WriteLine("static B()");
}
public static int b = methodB();
static int methodB()
{
Console.WriteLine($"initial B.b value {b}");
Console.WriteLine("B.methodB()");
return A.a;
}
}
public class A
{
static A()
{
Console.WriteLine("static A()");
}
public static int a = methodA();
static int methodA()
{
Console.WriteLine("A.methodA()");
return 5;
}
}
}
The console output of this is:
begin main()
initial B.b value 0
B.methodB()
A.methodA()
static A()
static B()
x = 5
This bears out what the spec says, in the following steps:
- static class B is accessed first, and static field initialization of variable b begins.
- default value can be read for B.b (0, as @Blindy says), before the static field initialization completes.
- it encounters a call to static class A, which hasn't been initialized yet.
- it pauses initialization of static class B and starts initialization of static class A
- static field initialization of variable a occurs, setting the value to 5 (You could also view the default value of 0 for this property before the static field initialization completes, but I believe the only place this could occur is inside
methodA
before thereturn
) - the static constructor is then called.
- initialization of static class B resumes
- static constructor in class B is called.
- finally the value of
5
is returned forB.b
, as desired/expected.
There doesn't seem to be any possibility of a race condition here, since the first time a static class is referenced, whatever is currently happening is immediately paused and the static class is fully initialized.
(At least in single-threaded code which is what OP is asking about. My initial thought is that multi-threaded code would also introduce no race conditions, but I couldn't say for sure)