My question comes after refactoring a class that contained only static methods to be declared as a static class, and experiencing weird issues when starting the application.
I have not performed any thorough investigation but it seems that some call being made from within the static constructor does not complete for some reason.
So, I would like to know where there are any pitfalls when using static constructors in C#? More specifically, are there any things that should be avoided at all cost and not be used from within the static constructor?
There are several pitfalls to static constructors. For example, if a static constructor throws an exception, you would continue getting a
TypeInitializationExceptionwhenever you access any of its members.In general, static classes should only be used in stateless scenarios where you won’t need any initialization. If your class needs to be initialized, you might be better off using the singleton pattern, which can be lazily initialized on first access:
Edit: I did some further reading up on the matter, and it appears that static constructors do cause deadlocks if you perform blocking operations (e.g. asynchronous callbacks or thread synchronization) within them.
The CLR internally uses locking to prevent type initializers (static constructors) from being executed multiple times concurrently. Thus, if your static constructor attempts to access another member of its declaring type from another thread, it would inevitably deadlock. Since “another member” could be an anonymous function declared as part of a PLINQ or TPL operation, these bugs can be subtle and hard to identify.
Igor Ostrovsky (MSFT) explains this in his Static constructor deadlocks article, providing the following example of a deadlock:
In the above example, the new thread needs to access the empty anonymous function,
{ }, defined as its callback. However, since the anonymous function is compiled as another private method ofMyClassbehind the scenes, the new thread cannot access it before theMyClasstype initializes. And, since theMyClassstatic constructor needs to wait for the new thread to complete first (because ofthread.Join()), a deadlock ensues.