C# and beforefieldinit
Think you can predict exactly when initialization happens in C#? It's more complicated than
you may expect...
The differences between static constructors and type initializers
Some implementations of the singleton pattern
rely on the behaviour of static constructors and type initializers, in
particular with respect to the time at which they are invoked.
The C# specification states:
The static constructor for a class executes at most once in a given application domain.
The execution of a static constructor is triggered by the first of the
following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.
|
The CLI specification (ECMA 335) states in section 8.9.5:
- A type may have a type-initializer method, or not.
- A type may 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):
- first access to any static or instance field of that type, or
- first invocation of any static, instance or virtual method of that type
|
The C# specification implies that no types with static constructors
should be marked with the beforefieldinit flag. Indeed,
this is upheld by the compiler, but with a slightly odd effect. I suspect
many programmers believe (as I did for a long time) that the following
classes were semantically equivalent:
class Test
{
static object o = new object();
}
class Test
{
static object o;
static Test()
{
o = new object();
}
}
The two classes are not, in fact, the same. They both have type
initializers - and the two type initializers are the same. However,
the first does not have a static constructor, whereas the second does.
This means that the first class can be marked as beforefieldinit
and have its type initializer invoked at any time before the first reference
to a static field in it. The static constructor doesn't even have to do
anything. This third class is equivalent to the second:
class Test
{
static object o = new object();
static Test()
{
}
}
I believe this is a source of significant confusion - particularly
in terms of singleton implementations.
The curious nature of beforefieldinit - lazy or not?
The beforefieldinit flag has a strange effect, in that it
can not only mean that a type initializer is invoked earlier than
that of an equivalent type without the flag - it could even be invoked
later, or not at all. Consider the following program:
using System;
class Test
{
public static string x = EchoAndReturn ("In type initializer");
public static string EchoAndReturn (string s)
{
Console.WriteLine (s);
return s;
}
}
class Driver
{
public static void Main()
{
Console.WriteLine("Starting Main");
Test.EchoAndReturn("Echo!");
Console.WriteLine("After echo");
string y = Test.x;
if (y != null)
{
Console.WriteLine("After field access");
}
}
}
The results of running the above are quite varied. The runtime could decide
to run the type initializer on loading the assembly to start with:
In type initializer
Starting Main
Echo!
After echo
After field access
Or perhaps it will run it when the static method is first run...
Starting Main
In type initializer
Echo!
After echo
After field access
Or even wait until the field is first accessed...
Starting Main
Echo!
After echo
In type initializer
After field access
(In theory, the type initializer could even be run after "Echo!" is displayed,
but before "After echo" is displayed. I would be very surprised to see any
runtime actually show this behaviour, however.) With a static constructor
in Test, only the middle of these is possible.
So, beforefieldinit can make the invocation of the type
initializer even lazier (the last result) or more eager (the first result).
I suspect even those developers who know of the existence of
beforefieldinit may be surprised by this.
The MSDN documentation for TypeAttributes.BeforeFieldInit
is particularly poor in this respect. It describes the flag like this:
| Specifies that calling static methods of the
type does not force the system to initialize the type. |
While this is true in the strictest possible sense, it certainly isn't
the complete story - it suggests that the flag only makes the initialization
lazier, not more eager.
It's worth noting that the v4 CLR behaves differently
compared with the v1 and v2 CLRs here - all of them obey they specification,
but the v4 CLR is genuinely lazy in many cases where earlier versions are
eager.
What should be done?
I propose the following changes:
- Static field initializers should be treated as if they were part
of a static constructor. In other words, any type with a static initializer
or an explicit static constructor should not (by default) be marked as
beforefieldinit. (Modification to the C# language specification.)
- There should be a way of overriding this default behaviour in code.
An attribute would be a perfectly reasonable solution to this. (Modification
to the C# language specification and addition of an attribute to the
standard library.)
- The documentation for TypeAttributes.BeforeFieldInit should be clarified
significantly. (Modification to MSDN documentation and ECMA 335.)
The above changes are all entirely backwards-compatible, and require no
CLI modification.
Further thoughts (after discussion on newsgroups)
The first of the above proposals is definitely the most controversial.
(The last isn't controversial at all, as far as I can see.) The reason is
performance. Not many classes actually need the behaviour assumed by
many C# programmers - most people need never know the difference, really.
The JIT compiler, however, cares quite a lot: if a static member is used
within a fairly tight loop, for instance, it makes a lot of sense to
initialise the type before entering the loop, knowing thereafter that the
type has already been initialised. When code is shared between app domains
etc, I gather this becomes even more important.
Making the performance of
existing code decrease by recompilation with a new version of the
framework would undoubtedly be unpopular. I'm therefore willing to concede
as a less-than-ideal proposal - indeed I've only left it in this page for
historical reasons (I dislike the idea of being a revisionist).
The second proposal, however, is still important - both to allow classes
which do have a static constructor to improve their performance with
BeforeFieldInit semantics if appropriate, and to allow classes which
currently only need a static constructor to get rid of BeforeFieldInit
semantics to achieve this aim in a more self-documenting manner.
(A junior developer is more likely to remove a static constructor
which appears to be a no-op than to remove an attribute they don't
fully understand.)