Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an instance of a class in its static constructor - why is it allowed?

Tags:

c#

Another question on SO inspired me to try this code in C#:

class Program
{
    static Program() 
    {
        new Program().Run();
    }

    static void Main(string[] args) { }

    void Run()
    {
        System.Console.WriteLine("Running");
    }
}

This prints "Running" when run.

I actually expected the compiler to complain about this. After all, if the class has not yet been initialized by the static constructor; how can we be sure that it is valid to call methods on it ?

So why does the compiler not restrict us from doing this ? Is there any important usage scenarios for this ?

Edit

I am aware of the Singleton pattern; the point in question is why I can call a method on the instance before my static constructor finishes. So far JaredPar's answer has some good reasoning about this.

like image 887
driis Avatar asked Sep 02 '10 18:09

driis


2 Answers

It is allowed because not allowing it would be a lot worse. Code like this would deadlock badly:

class A {
    public static readonly A a;
    public static readonly B b;
    static A() {
        b = new B();
        a = B.a;
    }
}

class B {
    public static readonly A a;
    public static readonly B b;
    static B() {
        a = new A();
        b = A.b;
    }
}

You are of course pointing a loaded gun at your foot.

This behavior is documented in the CLI Spec (Ecma 335) Partition II, Chapter 10.5.3.2 "Relaxed guarantees":

A type can be marked with the attribute beforefieldinit (§10.1.6) to indicate that the guarantees specified in §10.5.3.1 are not necessarily required. In particular, the final requirement above need not be provided: the type initializer need not be executed before a static method is called or referenced.

[Rationale: When code can be executed in multiple application domains it becomes particularly expensive to ensure this final guarantee. At the same time, examination of large bodies of managed code have shown that this final guarantee is rarely required, since type initializers are almost always simple methods for initializing static fields. Leaving it up to the CIL generator (and hence, possibly, to the programmer) to decide whether this guarantee is required therefore provides efficiency when it is desired at the cost of consistency guarantees.
end rationale]

The C# compiler indeed emits the beforefieldinit attribute on a class:

.class private auto ansi beforefieldinit ConsoleApplication2.Program
       extends [mscorlib]System.Object
{
   // etc...
}
like image 189
Hans Passant Avatar answered Oct 27 '22 01:10

Hans Passant


Slightly different question.

How would the compiler prevent you from doing this?

Sure it's very easy to detect in your sample, but what about this sample?

class Program {
  static void Fun() {
    new Program(); 
  }
  static Program() {
    Fun();
  }
}

The ways in which you can trick the compiler to allow this are virtually endless. Even if the compiler got all of the answers you could still beat it with reflection.

In the end though this is actually legal, if a bit dangerous, code in both C# and IL. It is safe to do this as long as you are careful about accessing static's from within this code. It also is helpful / possibly necessary for certain patterns like Singleton's

like image 25
JaredPar Avatar answered Oct 26 '22 23:10

JaredPar