【问题标题】:Controlling when the Static Constructor is called控制何时调用静态构造函数
【发布时间】:2009-12-11 07:06:54
【问题描述】:

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

我希望在运行时尽快调用静态构造函数,最好是在执行 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");

【问题讨论】:

    标签: c# attributes static-constructor


    【解决方案1】:

    正如 Marc 所说,如果我是你,我会在 Main 中明确地这样做。

    可以使用Type.TypeInitializer 属性显式调用类型的类型初始化程序并调用它。但是,这会导致它再次运行即使它已经运行过,这可能会产生意想不到的结果。

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

    CascadeOnUpdateAttribute.ConfigureDb4oFactories();
    

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

    【讨论】:

      【解决方案2】:

      如果您希望调用静态构造函数,则向该类型添加一个虚拟方法,并在代码开头调用它(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,这从来没有是一件好事......

      【讨论】:

      • 如果 Init 被内联,为什么会如此糟糕?
      • 如果它被内联到什么都没有,这让我想知道:它是否被调用;如果没有调用 - 是否调用了 .cctor?
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        我认为使用静态构造函数有异味;我会考虑重构您的代码以控制对 db4o 工厂的访问,这样您就不需要使用它了。

        【讨论】:

        • 我不会争论这种气味,但知道如何更快地调用构造函数仍然很有趣。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-05
        • 2012-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-08
        相关资源
        最近更新 更多