Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger a static constructor

code:

class Base<T,U> where T:Base<T,U>,new()  where U :class
{
    protected static U _val = null;
    internal static void ShowValue()
    {
        if(_val == null)new T(); //Without this line, it won't work as expected
        Console.WriteLine (_val);
    }
    internal static void Virtual()
    {
        Console.WriteLine ("Base");
    }
}
class Deriv :Base<Deriv,string>
{
    static Deriv()
    {
        _val = "some string value";
    }
    internal static new void Virtual ()
    {
        Console.WriteLine ("Deriv");
    }
}
 public static void Main (string[] args)
{
    Deriv.ShowValue();            
    Deriv.Virtual();
}

Thanks to the generics of .NET, I can create a bunch of specific classes reusing generic static methods defined in the generic base class. It can mimic inheritance polymorphism to some extent. But in order to initialize different version of static fields, I've to use static constructors. Unfortunately, we can't call them directly, therefore, we have to figure out a way to trigger it's invocation. The example given above showed a way. But I don't like either the instantiation,or the reflection approach. We also can't make a constraint on a static method of a generic parameter. So, I'd like to ask, if there is another way to do this kind of job!

Thanks beforehand!

~~~~~~~~~~~~~~~~

Some Conclusion (Maybe a little early):

It seems there is no workaround to deal with this kind of situation. I have to instantiate a subclass or use reflection. Considering the .cctors need merely be called once, I'm in favor of the reflection approach, because in some case, a new() constraint is just not a choice - like you're not supposed to expose the parameterless ctor to user.

After conducting further experiment, I find out that the .cctors may be called multi-times but only the first invocation will affect the setting of static fields. That's weird, but a good weirdness!

    class MyClass 
    {
        static int _val = 0;
        static MyClass()
        {
            _val++;
            Console.WriteLine (_val);
        }
    }
    public static void Main (string[] args)
    {
        ConstructorInfo ci = typeof(MyClass).TypeInitializer;
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
    }
//result:
//1
//1
//1
//1
like image 648
Need4Steed Avatar asked Aug 02 '11 06:08

Need4Steed


1 Answers

I would strongly advise you to rethink your design. Attempting to use this sort of workaround for "static inheritance" is fighting against some of the core designs of .NET.

It's unclear what bigger problem you're trying to solve, but trying "clever" code like this to simulate inheritance will lead to code which is very hard to maintain and diagnose in the longer term.

Without actually using a member of Deriv (or creating an instance of it), you basically won't trigger the static constructor. It's important to understand that Deriv.ShowValue() is basically converted into a call to

Base<Deriv, string>.ShowValue();

... so you're not actually calling anything on Deriv. Your calling code would actually be clearer if it were written that way.

EDIT: One other (clearly unfortunate) reason to avoid using type initializers explicitly is that there's a bug in .NET 4.5 which causes an exception to be thrown inappropriately in some cases. See my question on the topic for more information.

like image 148
Jon Skeet Avatar answered Sep 18 '22 23:09

Jon Skeet