控制何时调用静态构造函数

发布于 2024-08-14 18:46:59 字数 2467 浏览 1 评论 0 原文

在我的自定义属性的静态构造函数中,我在加载的程序集中搜索用我的属性修饰的所有类,并对它们执行一些操作。

我希望在运行时尽快调用静态构造函数,最好是在执行 static void Main() 入口点之前调用。

目前它仅在我之后调用对属性进行一些调用。我可以在程序的其他地方进行这样的调用,但理想情况下该属性的功能应该是独立的。

为了寻找答案,我在 MSDN

用户无法控制静态构造函数何时在程序中执行。

但肯定有一些棘手、狡猾或恶作剧的解决方法可以让静态构造函数尽快被调用。也许可以使用属性、反射或其他类型的魔法。 可以做到吗?

因为人们无疑会告诉我没有充分的理由去做我所要求的事情,所以我提出了我的目的和我的代码:我正在尝试使用属性来声明性配置一个 db4o 工厂。如果我的属性的静态构造函数在我已经建立连接之后被调用,那么它没有效果并且没有用处。因此,必须在我的程序有机会建立此类连接之前调用它。

[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);
        }
    }
}

更新:

我最终使用了带有静态方法的抽象属性。通过这种方式,我可以派生任意数量的属性,并且通过调用此方法将它们全部应用于指定的配置。

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);
        }
    }
}

以及调用站点:

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

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");

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

妥活 2024-08-21 18:46:59

正如 Marc 所说,如果我是你,我会在 Main 中明确执行此操作。

可以使用Type.TypeInitializer 属性并调用它。但是,这将导致它再次运行即使它已经运行过,这可能会产生意外的结果。

我个人会将该代码完全移出静态初始化程序。它是配置代码 - 为什么不将其设为可以显式调用的静态方法?我什至不确定是否将其放在属性类本身中,但至少显式调用:

CascadeOnUpdateAttribute.ConfigureDb4oFactories();

比调用虚拟方法或以其他方式强制类型初始化更清晰,只是为了获得副作用。

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.

空城之時有危險 2024-08-21 18:46:59

如果您希望调用静态构造函数,请向该类型添加一个虚拟方法,然后在代码开头调用它(Main 等);如果它是一个简单/空的方法,您可能希望将其标记为无内联等。

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");
    }
}

可以使用反射来调用静态构造函数,但我不推荐它;如果您使用反射,您实际上可以多次调用 .cctor,这永远不是一件好事......

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...

两仪 2024-08-21 18:46:59

您可以通过调用来避免静态虚拟方法

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

You can avoid the static dummy method by calling

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(CascadeOnUpdateAttribute).TypeHandle)
呆° 2024-08-21 18:46:59

我认为静态构造函数的使用很糟糕;我会考虑重构您的代码以控制对 db4o 工厂的访问,以便您不需要使用它。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文