首页 文章

如何运行静态构造函数?

提问于
浏览
38

我想在不创建实例的情况下执行类的静态构造函数(即我想“加载”类) . 我怎么做?

加分问题:.NET 4和旧版本之间有什么区别吗?

编辑:

  • 该类不是静态的 .

  • 我想在创建实例之前运行它,因为它需要一段时间才能运行,我想在第一次访问时避免这种延迟 .

  • 静态ctor初始化 private static readonly 字段因此无法在方法中运行 .

9 回答

  • -3

    其他答案非常好,但是如果你需要强制一个类构造函数在没有引用类型(即反射)的情况下运行,你可以使用:

    Type type = ...;
    System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
    
  • -1

    只需引用一个静态字段即可 . 这将强制您的静态初始化代码运行 . 例如:

    public class MyClass
    {
        private static readonly int someStaticField;
    
        static MyClass() => someStaticField = 1;
    
        // any no-op method call accepting your object will do fine
        public static void TouchMe() => GC.KeepAlive(someStaticField);
    }
    

    用法:

    // initialize statics
    MyClass.TouchMe();
    
  • -1

    只要发生以下任何一种情况,就会调用cctor(静态构造函数);

    • 您创建了该类的实例

    • 访问任何静态成员

    • 在此之前的任何时间,如果设置了BeforeFieldInit

    如果要显式调用cctor,假设您有其他静态成员,只需调用/访问它们即可 .

    如果你没有在你的cctor中做任何非常有趣的事情,编译器可能决定将它标记为 BeforeFieldInit ,这将允许CLR提前执行cctor的选项 . 这里有更详细的解释:http://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx

  • 4

    扩展Fábio的observations,以下简短完整的测试程序公开了TypeAttributes.BeforeFieldInit行为的JIT敏感细节,将 .NET 3.5 与最新版本(截至2017年底) .NET 4.7.1 进行比较,并且还展示了每个版本本身内构建类型变化的潜在危险 . [1]

    using System;
    using System.Diagnostics;
    
    class MyClass
    {
        public static Object _field = Program.init();
    
        public static void TouchMe() { }
    };
    
    class Program
    {
        static String methodcall, fieldinit;
    
        public static Object init() { return fieldinit = "fieldinit"; }
    
        static void Main(String[] args)
        {
            if (args.Length != 0)
            {
                methodcall = "TouchMe";
                MyClass.TouchMe();
            }
            Console.WriteLine("{0,18}  {1,7}  {2}", clrver(), methodcall, fieldinit);
        }
    };
    

    以下是在 { x86, x64 }{ Debug, Release } 的所有组合中运行此程序的控制台输出 . 我手动添加了一个delta符号 Δ (程序没有发出)以突出显示两个.NET版本之间的差异 .

    .NET 2.0 / 3.5 2.0.50727.8825 x86调试2.0.50727.8825 x86调试TouchMe fieldinit 2.0.50727.8825 x86发布fieldinit 2.0.50727.8825 x86发布TouchMe fieldinit 2.0.50727.8825 x64调试2.0.50727.8825 x64调试TouchMe fieldinit 2.0.50727.8825 x64发布2.0 . 50727.8825 x64发布TouchMe fieldinit .NET 4.7.1 4.7.2556.0 x86调试4.7.2556.0 x86调试TouchMe fieldinit 4.7.2556.0 x86发布Δ4.7.2556.0x86发布TouchMeΔ4.7.2556.0x64调试4.7.2556.0 x64调试TouchMe fieldinit 4.7.2556.0 x64版本4.7.2556.0 x64释放TouchMeΔ

    正如介绍中所述,或许比版本 2.0 / 3.54.7 deltas更有趣的是当前.NET版本中的差异,因为它们表明,尽管现在的字段初始化行为在 x86 和_1578904之间比它使用的更加一致在今天的 DebugRelease 构建之间,仍然可能会遇到运行时字段初始化行为的显着差异 .

    语义将取决于你是否恰好在类上调用了一个不相交或看似无关的静态方法,所以如果这样做会为你的整体设计引入一个bug,那么它很可能是非常神秘且难以追踪的 .


    Notes
    1.以上程序使用以下实用程序功能显示当前 CLR 版本:

    static String clrver()
    {
        var s = typeof(Uri).Assembly.Location;
        return FileVersionInfo.GetVersionInfo(s).ProductVersion.PadRight(14) +
            (IntPtr.Size == 4 ? " x86 " : " x64 ") +
    #if DEBUG
            "Debug  ";
    #else
            "Release";
    #endif
    }
    
  • 3

    访问静态方法时,并不总是调用静态构造函数!

    我注意到如果在基类中调用静态方法,则不会调用超类的静态构造函数 . 这种意想不到的行为多次被咬伤 .

  • 96

    你也可以这样做:type.TypeInitializer.Invoke(null,null);

  • 16

    没有必要这样做,static constructor的重点是它在第一次访问时首次初始化时运行一次 . 如果要按需运行某些内容,请考虑将初始化代码添加到构造函数调用的公共方法中 . 然后,您可以随时调用此方法 . 但我不确定你为什么要这样做?

  • 0

    正如其他人所说,静态构造函数会自动运行 . 如果你需要明确,也许你应该将它重构为一个可以显式运行的静态方法?

    当然,显式调用静态方法也可以确保已经执行了静态构造函数 .

    edit

    any static members are referenced时运行静态构造函数 . 你可以简单地创建一个名为 initialize 的虚方法,除了确保框架调用静态构造函数之外什么也没做 .

  • 0

    静态构造函数在您第一次访问该类时自动运行 . 没有必要(或能力)自己“运行”它 .

相关问题