As stated in ECMA-335 (CLI cpecification), part I, section 8.9.5:
The semantics of when and what triggers execution of such type initialization methods, is as follows:
- A type can have a type-initializer method, or not.
- A type can be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit).
- If marked BeforeFieldInit then the type’s initializer method is executed at, or sometime before, first access to any static field defined for that type.
If not marked BeforeFieldInit then that type’s initializer method is executed at (i.e., is triggered by):
a. first access to any static field of that type, or
b. first invocation of any static method of that type, or
c. first invocation of any instance or virtual method of that type if it is a value type or
d. first invocation of any constructor for that type.
Also, as you can see from the Michael's code above, the TestClassProxy
has only one static field: _proxy_handlers
. Notice, that it is used only two times:
- In the instance constructor
- And in the static field initializer itself
So when BeforeFieldInit
is specified, type-initializer will be called only once: in the instance constructor, right before the first access to _proxy_handlers
.
But if BeforeFieldInit
is omitted, CLR will place the call to the type-initializer before every TestClassProxy's
static method invocation, static field access, etc.
In particular, the type-initializer will be called on every invocation of s_0_Test
and s_1_Test<T>
static methods.
Of course, as stated in ECMA-334 (C# Language Specification), section 17.11:
The static constructor for a non-generic class executes at most once in a given application domain. The static constructor for a generic class declaration executes at most once for each closed constructed type constructed from the class declaration (§25.1.5).
But in order to guarantee this, CLR have to check (in a thread-safe manner) if the class is already initialized, or not.
And these checks will decrease the performance.
PS: You might be surprised that performance issues will gone once you change s_0_Test
and s_1_Test<T>
to be instance-methods.