Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eager static constructor

I've read a few Stack Overflow questions and answers, along with some blog posts (including Jon Skeet's lazy singleton initialization), and they all seem to focus on making initialization as lazy as possible. It seems there are basically two options for static initialization:

  • At first reference to an instance or static member of a class
  • At an unspecified time between the start of the program and the first reference.

Is there any way to get a static constructor (or some form of initialization code) to run for a particular class (or classes) at the start of the program?

Context: Our library will be parsing incoming XML and return objects. The type of object returned depends on the XML element being parsed. We provide two simple classes: one is a very basic class that allows access to the attribues and inner XML (as a string), with no features; the second is for a specific type of object, and provides constraint checking and more context-specific names for accessing/editing values.

The parser determines how to parse a particular XML element by looking though its list of parsers. If it has a parser for the element it's parsing (determined by name), it uses that. If it doesn't, or if it fails, it falls back on the basic parser.

Developers using our library are highly likely to write their own classes for particular XML elements. Rather than having them manually add the parse method of each class to the list at the start of each application, it would be great if each class could have a static constructor that adds its own parser to the list, such that simply including the class in the project will register it. However, static constructors won't fire until the class is actually referenced, and we have no guarantee that every such class will be referenced before parsing begins.

Is there any way to guarantee some initializer fires for each of these classes at application start? The benefit of this would be simply including the classes in the project and not having to manually add each parse method to our parser's list at runtime, which is a fairly small convenience, so for the benefit to be worth the work, the solution needs to be pretty simple and straightforward to implement.

like image 833
yoozer8 Avatar asked Mar 22 '13 18:03

yoozer8


People also ask

What is a static constructor?

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It is called automatically before the first instance is created or any static members are referenced.

Can you have a static constructor?

No, we cannot define a static constructor in Java, If we are trying to define a constructor with the static keyword a compile-time error will occur. In general, static means class level. A constructor will be used to assign initial values for the instance variables.

Why static constructor is Parameterless in C#?

When a data member is shared among different instances it is imperative that data should be consistent among all the instances of the class. And also there is no way to call static constructor explicitly. Therefore the purpose of having a parameterized static constructor is useless.

What is difference between static constructor and private constructor?

Static constructor will be called first time when the class is referenced. Static constructor is used to initialize static members of the class. In the non static class the private or public constructor will not be called. Static members will not be initialized either by private or public constructor.


2 Answers

Is there any way to get a static constructor (or some form of initialization code) to run for a particular class (or classes) at the start of the program?

It sounds like you want some sort of "module or assembly initializer". I don't think such a thing exists in IL (although I could be wrong) and it definitely doesn't exist in C#.

You could always create some sort of attribute and then use reflection to find all the types decorated with that attribute, and initialize them explicitly. (Note that it becomes trickier with generic types... you'd probably want to limit it to non-generic ones.)

EDIT: I've found a couple more options:

  • A post-build tool which creates an initializer which looks for ModuleInitializer.Run in any namespace, using Mono Cecil
  • A module initializer add-on for Fody

EDIT: With more context, I suspect any cure would be worse than the disease, as it were. Any developer who wants to write a reflection-based "find all parsers with this attribute" (or similar) doesn't have very much work to do, but I don't think you want to interfere with their own application start-up.

To make others' lives easier without imposing anything, you could always include that reflection part yourself:

public static void RegisterAllParsers(Assembly assembly)

... which would probably be attribute-based. It could only sensibly pick up static parse methods, of course - if any developer had a factory which could parse in different ways depending on initialization of the factory, you couldn't easily register that automatically.

The developer would then need to call:

LibraryClass.RegisterAllParsers(typeof(SomeTypeInProgram).Assembly);

on start-up. That's probably not too hard to remember to do - and most applications only have a single entry point, or at least some common start-up code.

like image 147
Jon Skeet Avatar answered Sep 29 '22 21:09

Jon Skeet


Afaik there is no way to do it explicitly, but you could create something like the following (I warn you now, its ugly and not fast):

[System.AttributeUsage(System.AttributeTargets.Class |
                   System.AttributeTargets.Struct)]
public class AppInitialized : System.Attribute
{
    private MethodInfo _mInfo;

    public AppInitialized(Type t, String method)
    {
        _mInfo = t.GetMethod(method, BindingFlags.Static | BindingFlags.Public);
    }

    public void Initialize()
    {
        if (_mInfo != null)
            _mInfo.Invoke(null, null);
    }
}

[AppInitialized(typeof(InitializeMe), "Initialize")]
public class InitializeMe
{
    public static void Initialize()
    {
        Console.WriteLine("InitializeMe initialized");
    }
}

And then when your application loads, use something like this to initialize everything with the custom attribute:

foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
    var a = Attribute.GetCustomAttribute(type, typeof(AppInitialized), true) 
        as AppInitialized;
    if (a != null)
        a.Initialize();
}
like image 28
FlyingStreudel Avatar answered Sep 29 '22 21:09

FlyingStreudel