Controlling when the Static Constructor is called

纵饮孤独 提交于 2019-12-10 06:08:28

问题


In my custom attribute's static constructor, I search the loaded assembly for all classes decorated with my attribute and perform some action on them.

I would like the static constructor to be called as soon as possible during runtime, preferably before execution of the static void Main() entry point.

Currently it only gets called after I make some call to the attribute. I could make such a call elsewhere in my program, but ideally the attribute's functionality would be self-contained.

Looking for answers, I read this on MSDN:

The user has no control on when the static constructor is executed in the program.

But surely there is some tricky, sly, or mischievous workaround to get a static constructor to be called ASAP. Perhaps an attribute, reflection, or some other kind of magic could be used. Can it be done?

Because people would undoubtedly tell me that there is no good reason to do what I ask, I present my purpose and my code: I am trying to use attributes to declaratively configure a db4o factory. If my attribute's static constructor is called after I've already established a connection, then it has no effect and is useless. Therefore it must be called before my program gets a chance to establish such a connection.

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed public class CascadeOnUpdateAttribute : Attribute
{
    public bool Flag { get; private set; }

    public CascadeOnUpdateAttribute() : this(true) { }

    public CascadeOnUpdateAttribute(bool flag)
    {
        Flag = flag;
    }

    static CascadeOnUpdateAttribute()
    {
        var targets = from assembly in AppDomain.CurrentDomain.GetAssemblies()
          from type in assembly.GetTypes()
          from attribute in type.GetCustomAttributes(typeof(CascadeOnUpdateAttribute), false).Cast<CascadeOnUpdateAttribute>()
          select new { Type = type, Cascade = attribute.Flag };

        foreach (var target in targets)
        {
            Db4oFactory.Configure().ObjectClass(target.Type).CascadeOnUpdate(target.Cascade);
        }
    }
}

Update:

I ended up using an abstract attribute with a static method. This way I can derive as many attributes as I like and they will all be applied to a specified config by calling this one method.

public abstract class Db4oAttribute : Attribute
{
    public abstract void Configure(IConfiguration config, Type type);

    public static void ApplyAttributes(IConfiguration config)
    {
        var targets = from assembly in AppDomain.CurrentDomain.GetAssemblies()
          from type in assembly.GetTypes()
          from attribute in type.GetCustomAttributes(typeof(Db4oAttribute), false).Cast<Db4oAttribute>()
          select new { Type = type, Attribute = attribute };

        foreach (var target in targets)
        {
            target.Attribute.Configure(config, target.Type);
        }
    }
}

And the call site:

Db4oAttribute.ApplyAttributes(Db4oFactory.Configure());
_db = Db4oFactory.OpenFile("Test.db4o");

回答1:


As Marc says, I would do it explicitly in Main if I were you.

You can invoke the type initializer for a type explicitly using the Type.TypeInitializer property and invoking it. However, this will cause it to run again even if it's already been run which could produce unexpected results.

I would personally move that code out of the static initializer completely. It's configuration code - why not just make it a static method which you can call explicitly? I'm not even sure I'd have it in the attribute class itself, but at least explicitly calling:

CascadeOnUpdateAttribute.ConfigureDb4oFactories();

is clearer than calling a dummy method or forcing type initialization some other way, just to get a side effect.




回答2:


If you want the static constructor to get called, then add a dummy method to the type and simply call it at the start of your code (Main etc); if it is a trivial / empty method you might want to mark it for no inlining etc.

class SomeType {
    static SomeType() {
        Console.WriteLine("SomeType.cctor");
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void Init() { }
}

static class Program {
    static void Main() {
        SomeType.Init();
        Console.WriteLine("hi");
    }
}

You can use reflection to call the static constructor, but I don't recommend it; if you use reflection you can actually call the .cctor multiple times, and that is never a good thing...




回答3:


You can avoid the static dummy method by calling

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(CascadeOnUpdateAttribute).TypeHandle)



回答4:


I think the use of the static constructor smells; I would consider refactoring your code to control access to the db4o factory so that you don't need to use it.



来源:https://stackoverflow.com/questions/1886339/controlling-when-the-static-constructor-is-called

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!