Search code examples
c#.netdictionaryconstructorstatic-constructor

why Static constructors not called first and i got exception when use declared variable?


From MSDN

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced

MSDN Link

Now came to my problem:

public static class DateFormat
{
    private static List<string> DateFormats = new List<string>();

    public static string DateSeparator  { get; set; } = "/";

    public static string Current { get; set; } = DateFormats[1]; // error here

    static DateFormat()
    {
        DateFormats.Add("yyyy{0}MM{0}dd HH{1}mm{1}ss");
        DateFormats.Add("yyyy{0}MM{0}dd hh{1}mm{1}ss");
    }
}

As you see above when calling DateFormats[1] error

"The type initializer for 'DateFormat' threw an exception."

Is constructor should calling static constructor first? so that dictionary will filled then any call to variable which use it will be ok.


Solution

  • This behaviour is documented here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

    Specifically, this section:

    If static field variable initializers are present in the class of the static constructor, they will be executed in the textual order in which they appear in the class declaration immediately prior to the execution of the static constructor.

    Since static field variable initializers are indeed present, they will be initialised BEFORE the static constructor, which is why you are seeing that error:

    public static string Current { get; set; } = DateFormats[1]; 
    

    will be executed before

    static DateFormat()
    {
        DateFormats.Add("yyyy{0}MM{0}dd HH{1}mm{1}ss");
        DateFormats.Add("yyyy{0}MM{0}dd hh{1}mm{1}ss");
    }
    

    and thus of course, the DateFormats list is still empty at the point that DateFormats[1] is executed.

    To solve this, just initialise Current in the static constructor:

    public static class DateFormat
    {
        static DateFormat()
        {
            DateFormats.Add("yyyy{0}MM{0}dd HH{1}mm{1}ss");
            DateFormats.Add("yyyy{0}MM{0}dd hh{1}mm{1}ss");
    
            Current = DateFormats[1];
        }
    
        private static List<string> DateFormats = new List<string>();
    
        public static string DateSeparator { get; set; } = "/";
    
        public static string Current { get; set; }
    }