39

The documentation on static constructors in C# says:

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

That last part (about when it is automatically called) threw me for a loop; until reading that part I thought that by simply accessing a class in any way, I could be sure that its base class's static constructor had been called. Testing and examining the documentation have revealed that this is not the case; it seems that the static constructor for a base class is not guaranteed to run until a member of that base class specifically is accessed.

Now, I guess in most cases when you're dealing with a derived class, you would construct an instance and this would constitute an instance of the base class being created, thus the static constructor would be called. But if I'm only dealing with static members of the derived class, what then?

To make this a bit more concrete, I thought that the code below would work:

abstract class TypeBase
{
    static TypeBase()
    {
        Type<int>.Name = "int";
        Type<long>.Name = "long";
        Type<double>.Name = "double";
    }
}

class Type<T> : TypeBase
{
    public static string Name { get; internal set; }
}

class Program
{
    Console.WriteLine(Type<int>.Name);
}

I assumed that accessing the Type<T> class would automatically invoke the static constructor for TypeBase; but this appears not to be the case. Type<int>.Name is null, and the code above outputs the empty string.

Aside from creating some dummy member (like a static Initialize() method that does nothing), is there a better way to ensure that a base type's static constructor will be called before any of its derived types is used?

If not, then... dummy member it is!

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • Unless this is sample code just for the sake of the question, couldn't you just do `public static string Name { get { return typeof(T).Name; } }`? – Matt Greer Jan 10 '11 at 23:07
  • @Matt: Sorry, I should have made that clear, you're right: this is sample code just for the sake of the question. – Dan Tao Jan 10 '11 at 23:14
  • Using an abstract class as a concrete example? ;-) – Geeb Oct 06 '17 at 09:14

6 Answers6

28

You may call static constructor explicity, so you will not have to create any methods for initialization:

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);

You may call it in static constructor of derived class.

petro.sidlovskyy
  • 5,075
  • 1
  • 25
  • 29
  • 4
    +1 This seems to work. As with any static initialization order solution, I'm dubious as to whether it is bulletproof, but at least it doesn't seems to run the static ctor twice if you invoke this twice, in a simple single-threaded app. – Merlyn Morgan-Graham Jan 11 '11 at 03:39
  • Any idea of the performance of this method? Does it use reflection and/or is it fast if the static constructor has already been called? – devios1 Feb 22 '11 at 04:27
  • This is the way to do it - to replicate base() in static constructors just call RuntimeHelpers. RunClassConstructor(typeof(TypeBase).TypeHandle); at the beginning of each constructor. – Tod Thomson Jan 30 '14 at 08:00
20

As others have noted, your analysis is correct. The spec is implemented quite literally here; since no member of the base class has been invoked and no instance has been created, the static constructor of the base class is not called. I can see how that might be surprising, but it is a strict and correct implementation of the spec.

I don't have any advice for you other than "if it hurts when you do that, don't do that." I just wanted to point out that the opposite case can also bite you:

class Program 
{
  static void Main(string[] args)
  {      
    D.M();
  }      

}
class B 
{ 
  static B() { Console.WriteLine("B"); }
  public static void M() {}
} 
class D: B 
{ 
  static D() { Console.WriteLine("D"); }
}

This prints "B" despite the fact that "a member of D" has been invoked. M is a member of D solely by inheritance; the CLR has no way of distinguishing whether B.M was invoked "through D" or "through B".

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
14

The rules here are very complex, and between CLR 2.0 and CLR 4.0 they actually changed in subtle and interesting ways, that IMO make most "clever" approaches brittle between CLR versions. An Initialize() method also might not do the job in CLR 4.0 if it doesn't touch the fields.

I would look for an alternative design, or perhaps use regular lazy initialization in your type (i.e. check a bit or a reference (against null) to see if it has been done).

Scorpion
  • 784
  • 7
  • 25
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I was able to get a call to a dummy Initialize() member that didn't touch the fields to with with CLR 4.0. As per the code I posted in my answer. – Joshua Rodgers Jan 10 '11 at 23:16
  • @Joshua: So long as the base class has a static constructor -- not just field initialisers -- then calling `Initialize` *is* guaranteed to call that constructor because the type won't be marked `beforefieldinit`. As the spec says, the static constructor will be called *"...before the first instance is created or any static members are referenced"*. And since the question is asking about how to force the static constructor to fire then we can assume that there'll be one. – LukeH Jan 10 '11 at 23:42
  • Having said that, relying on obscure corners of the spec to guarantee functionality isn't a great idea, especially when someone unfamiliar with that corner of the spec comes along to update the code a few years down the line. Much better to do something explicit and obviously correct, as you suggest. – LukeH Jan 10 '11 at 23:45
  • I *had* been trying to implement the idea I suggested [in this answer](http://stackoverflow.com/questions/4620476/generic-that-takes-only-numeric-types-int-double-etc/4622837#4622837) based on SLaks's suggestion of using a generic static type instead of a dictionary. My thinking was that deriving from a non-generic type and using that base type's static constructor to initialize the "dictionary" would be an elegant solution; now that I realize it would require a hack to utilize such a constructor (e.g., a dummy member), I see that you're right (cont.)... – Dan Tao Jan 11 '11 at 00:20
  • ...and a different design altogether would make more sense. What I'm thinking I'll do is just ditch the "base type" idea completely and simply use a separate `internal` class to perform the initialization I want. – Dan Tao Jan 11 '11 at 00:24
  • 1
    Second link is dead :( – Erik Philips Nov 16 '16 at 22:27
3

In all of my testing, I was only able to get a call to a dummy member on the base to cause the base to call its static constructor as illustrated:

class Base
{
    static Base()
    {
        Console.WriteLine("Base static constructor called.");
    }

    internal static void Initialize() { }
}

class Derived : Base
{
    static Derived()
    {
        Initialize(); //Removing this will cause the Base static constructor not to be executed.
        Console.WriteLine("Derived static constructor called.");
    }

    public static void DoStaticStuff()
    {
        Console.WriteLine("Doing static stuff.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived.DoStaticStuff();
    }
}

The other option was including a static read-only member in the derived typed that did the following:

private static readonly Base myBase = new Base();

This however feels like a hack (although so does the dummy member) just to get the base static constructor to be called.

Joshua Rodgers
  • 5,317
  • 2
  • 31
  • 29
3

I almost alway regret relying on something like this. Static methods and classes can limit you later on. If you wanted to code some special behavior for your Type class later you would be boxed in.

So here is a slight variation on your approach. It is a bit more code but it will allow you to have a custom Type defined later that lets you do custom things.

    abstract class TypeBase
    {
        private static bool _initialized;

        protected static void Initialize()
        {
            if (!_initialized)
            {
                Type<int>.Instance = new Type<int> {Name = "int"};
                Type<long>.Instance = new Type<long> {Name = "long"};
                Type<double>.Instance = new Type<double> {Name = "double"};
                _initialized = true;
            }
        }
    }

    class Type<T> : TypeBase
    {
        private static Type<T> _instance;

        public static Type<T> Instance
        {
            get
            {
                Initialize();
                return _instance;
            }
            internal set { _instance = value; }
        }

        public string Name { get; internal set; }
    }

Then later when you get to adding a virtual method to Type and want a special implementation for Type you can implement thus:

class TypeInt : Type<int>
{
    public override string Foo()
    {
        return "Int Fooooo";
    }
}

And then hook it up by changing

protected static void Initialize()
{
      if (!_initialized)
      {
          Type<int>.Instance = new TypeInt {Name = "int"};
          Type<long>.Instance = new Type<long> {Name = "long"};
          Type<double>.Instance = new Type<double> {Name = "double"};
          _initialized = true;
       }
}

My advice would be to avoid static constructors - it is easy to do. Also avoid static classes and where possible static members. I am not saying never, just sparingly. Prefer a singleton of a class to a static.

Neil
  • 1,605
  • 10
  • 15
0

Just an idea, you can do something like this:

    abstract class TypeBase
    {
        static TypeBase()
        {
            Type<int>.Name = "int";
            Type<long>.Name = "long";
            Type<double>.Name = "double";
        }
    }

    class Type<T> : TypeBase
    {
        static Type() 
        {
            new Type<object>();
        }

        public static string Name { get; internal set; }
    }

    class Program
    {
        Console.WriteLine(Type<int>.Name);
    }
HABJAN
  • 9,212
  • 3
  • 35
  • 59