Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to force static fields to be initialized in C#?

Consider the following code:

class Program
{
    static Program() {
        Program.program1.Value = 5;
    }

    static List<Program> values = new List<Program>();
    int value;
    int Value
    {
        get { return value; }
        set { 
            this.value = value;
            Program.values.Add(this);
        }
    }

    static Program program1 = new Program { value = 1 };
    static Program program2 = new Program { value = 2 };
    static Program program3 = new Program { value = 3 };

    static void Main(string[] args)
    {
        if (Program.values.Count == 0) Console.WriteLine("Empty");
        foreach (var value in Program.values)
            Console.WriteLine(value.Value);
        Console.ReadKey();
    }
}

It prints only the number 5, and if removed the code in the static constructor, it prints "Empty".

Is there a way to force static fields to be initialized even whether not used yet?

I need to have a static property named Values with returns all instances of the referred type.

I tried some variations of this code and some works for some types but doesn't for others.

EDIT: THE SAMPLE ABOVE IS BROKEN, TRY THIS ONE:

class Subclass<T> {
    static Subclass()
    {
        Values = new List<Subclass<T>>();
    }
    public Subclass()
    {
        if (!Values.Any(i => i.Value.Equals(this.Value)))
        {
            Values.Add(this);
        } 
    }

    public T Value { get; set; }

    public static List<Subclass<T>> Values { get; private set; }
}

class Superclass : Subclass<int>
{
    public static Superclass SuperclassA1 = new Superclass { Value = 1 };
    public static Superclass SuperclassA2 = new Superclass { Value = 2 };
    public static Superclass SuperclassA3 = new Superclass { Value = 3 };
    public static Superclass SuperclassA4 = new Superclass { Value = 4 }; 
}

class Program
{
    static void Main(string[] args)
    {
        //Console.WriteLine(Superclass.SuperclassA1); //UNCOMMENT THIS LINE AND IT WORKS
        foreach (var value in Superclass.Values)
        {
            Console.WriteLine(value.Value);
        }
        Console.ReadKey();
    }
}
like image 332
Rafael Romão Avatar asked Jan 28 '10 13:01

Rafael Romão


People also ask

How do you initialize a static field?

The only way to initialize static final variables other than the declaration statement is Static block. A static block is a block of code with a static keyword. In general, these are used to initialize the static members. JVM executes static blocks before the main method at the time of class loading.

Can static variables be initialised?

A static variable in a block is initialized only one time, prior to program execution, whereas an auto variable that has an initializer is initialized every time it comes into existence. A static object of class type will use the default constructor if you do not initialize it.

Why static variables Cannot be initialized?

The reason a static object cannot be initialized by a non constant value is related to the fact that the initialization of a static object is done "prior to program startup" (C99, 6.2. 4p3).

Is static variable initialized by default?

Global and static variables are initialized to their default values because it is in the C or C++ standards and it is free to assign a value by zero at compile time. Both static and global variable behave same to the generated object code.


3 Answers

The answer to your question is 'well, yes'. But one of the two ways of "forcing" it is what you're already doing.

The relevant section in the language spec is Static constructors, and specifically:

The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • An instance of the class is created.
  • Any of the static members of the class are referenced.

If a class contains the Main method (Section 3.1) in which execution begins, the static constructor for that class executes before the Main method is called. If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor.

like image 149
Christoffer Lette Avatar answered Sep 19 '22 17:09

Christoffer Lette


There is actually a way to force the initialization of the properties in this case. The change requires adding a type parameter to the base class to represent the future subclass of the base class that will contain the fields to be initialized. Then we can use the RuntimeHelpers.RunClassConstructor to ensure that the sub class static fields are initialized.

The following will yield the results that you are looking for:

class Subclass<TSubclass, T> 
{
    static Subclass()
    {
        Values = new List<Subclass<TSubclass, T>>();
        // This line is where the magic happens
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle);
    }
    public Subclass()
    {
        if (!Values.Any(i => i.Value.Equals(this.Value)))
        {
            Values.Add(this);
        } 
    }

    public T Value { get; set; }

    public static List<Subclass<TSubclass, T>> Values { get; private set; }
}

class Superclass : Subclass<Superclass, int>
{
    public static Superclass SuperclassA1 = new Superclass { Value = 1 };
    public static Superclass SuperclassA2 = new Superclass { Value = 2 };
    public static Superclass SuperclassA3 = new Superclass { Value = 3 };
    public static Superclass SuperclassA4 = new Superclass { Value = 4 }; 
}

public class Program
{
    public static void Main()
    {
        foreach (var value in Superclass.Values)
        {
            Console.WriteLine(value.Value);
        }
        Console.ReadKey();
    }
}

What is happening is, the call to RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle) forces the static constructor of TSubclass to execute if it has not already run. This ensures that the static fields have initialized first as per this line from https://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx :

If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor.

Here is a dotnetfiddle demonstrating it working:

https://dotnetfiddle.net/MfXzFd

like image 21
Tyree Jackson Avatar answered Sep 20 '22 17:09

Tyree Jackson


But you're never setting the property -- instead you're setting the backing field directly, so not going through your logic to add to the static list when creating program1, program2 and program3.

i.e. you need to change:

    static Program program1 = new Program { value = 1 };
    static Program program2 = new Program { value = 2 };
    static Program program3 = new Program { value = 3 };

to:

    static Program program1 = new Program { Value = 1 };
    static Program program2 = new Program { Value = 2 };
    static Program program3 = new Program { Value = 3 };
like image 37
Rowland Shaw Avatar answered Sep 20 '22 17:09

Rowland Shaw