Skip to content

Instantly share code, notes, and snippets.

@xiaomi7732
Created January 5, 2025 17:51
Show Gist options
  • Save xiaomi7732/d781bbaf39b0425df69d4c41ce4a09f2 to your computer and use it in GitHub Desktop.
Save xiaomi7732/d781bbaf39b0425df69d4c41ce4a09f2 to your computer and use it in GitHub Desktop.
Static member initialization order caused bug

Hidden Bug Caused by Static Member Initialization

I’ve encountered this bug a few times, and I wanted to share it with others.

The bug itself is simple once broken down, but it can be quite deceptive in real code. Let’s dive into the code and I’ll explain the issue step by step.

Example Code with the Bug

using System;
					
public sealed class Program
{
    private Program(){}
    public static Program Instance { get; } = new Program();
    private static int _value = 100;
    public int ExposedValue { get; } = _value;

    public static void Main()
    {
        Console.WriteLine(Program.Instance.ExposedValue);
    }
}

Explanation of the Bug

In the code above, we’re creating a singleton of the Program class. It encapsulates a static field, _value, which is initialized to 100. The class exposes an instance property, ExposedValue, which expects to return the value 100.

However, when running the Main method, the output is 0 instead of 100. Let's break down why this happens.

Key Concepts to Understand

  1. Static Initializers Run in the Order They Appear in Code

    Consider the following example:

    static int value2 = value1; // What's the value of `value2`? => 0.
    static int value1 = 200;

    Since static members are initialized in the order they appear, value2 is assigned 0 because value1 hasn’t been initialized yet.

  2. Static Members Are Usually Initialized When Accessed

    For instance, in the following class:

    public class TestClass
    {
        public int ExposedValue { get; } = _value;
        public static int _value = 100;
    }

    When you access (new TestClass()).ExposedValue, it will return 100 as expected. This happens because the static member _value is initialized before being used in the instance property ExposedValue.

Why Does It Return 0 in the Buggy Code?

Now, let’s focus on the line causing the issue:

public static Program Instance { get; } = new Program();

Here, the Instance property is static, and it calls the constructor of Program. The constructor initializes the instance members, including ExposedValue.

However, due to the way static initializers work (they run in order of appearance in the source code), the _value field is still at its default value (0) when it’s used to initialize ExposedValue. This happens because _value is initialized later, after the Instance property’s static initializer is called.

Thus, ExposedValue gets assigned the value 0 instead of 100.

Let me also call out, on the side, that is also an unusual situation when a constructor is called before a static constructor.

Fixing the Issue

To fix the bug, we need to change the order of initialization so that the static fields are initialized in the correct order. Here’s the corrected version of the code:

using System;
					
public sealed class Program
{
    private Program(){}

    // The order of these two lines is flipped:
    private static int _value = 100;
    public static Program Instance { get; } = new Program();
    
    public int ExposedValue { get; } = _value;

    public static void Main()
    {
        Console.WriteLine(Program.Instance.ExposedValue);
    }
}

Now, the ExposedValue will correctly return 100 as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment