I’ve got an ASP.NET web app that is starting to show some very strange behavior. Here’s some example code:
// in Bar.cs
public class Bar {
public static Baz baz = Something.Step2();
}
// in Global.asax
public void Application_Start(...) {
Something.Step1();
}
The short version of the story is this: On some machines, Something.Step2 is executed before Something.Step1 and is throwing an unhandleable exception. On other machines, Step1 correctly executes before Step2. Global.asax and all the objects it uses do not refer to Bar at all.
When are static fields supposed to execute in relation to other programming elements? Why would two machines (both Win7 64-bit, both with .NET 4.0, same IIS version, etc) execute things in different orders? The order is consistent on each machine too. On my machine, it always executes Step2 before Step1, but on my coworker’s machine it always executes Step1 before Step2.
Help very much appreciated.
Update I’ve found the root cause why my static field is being accessed. Class “Bar” from my example is actually a custom authentication module, and is referenced in web.config as the Authentication handler under System.webServer. If I remove that line from web.config, my system calls Step1 first and never calls Step2 at all. My question changes subtly to: “Why does web.config cause my static initializers to fire, and why does it cause them to fire before Application_Start executes?”
Static field initializers, when you don’t have a static constructor, are initialized at an "implementation-dependent" time prior to the first access. This means that they can be initialized at any time – you can’t rely on the initialization of
Bar.Bazto occur the first time you useBar.Adding a static constructor to
Barwill change the behavior of this to be more of what you’re expecting.For details, read the C# language spec, section 10.5.5.1:
Also, see 10.2, for details on static constructors:
That being said, if there is deterministic initialization required, I would strongly recommend adding a static constructor to
Something, and doing the initialization properly there. In addition to providing a guarantee of the initialization order in this case, it will prevent usage ofSomethingby another class from causing your code to break in the future.