如何运行静态构造函数?

时间:2010-04-16 15:09:46

标签: c# .net static-constructor

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

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

编辑:

  • 班级不是静态的。
  • 我想在创建实例之前运行它,因为它需要一段时间才能运行,我想在第一次访问时避免这种延迟。
  • 静态ctor初始化private static readonly字段,因此无法在方法中运行。

10 个答案:

答案 0 :(得分:99)

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

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

答案 1 :(得分:16)

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

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

答案 2 :(得分:4)

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

  1. 您创建了一个类
  2. 的实例
  3. 访问任何静态成员
  4. 在此之前的任何时间,如果设置了BeforeFieldInit
  5. 如果要显式调用cctor,假设你有其他静态成员,只需调用/访问它们。

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

答案 3 :(得分:3)

扩展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 Debug
  2.0.50727.8825 x86 Debug TouchMe fieldinit
  2.0.50727.8825 x86 Release fieldinit
  2.0.50727.8825 x86 Release TouchMe fieldinit
  2.0.50727.8825 x64 Debug
  2.0.50727.8825 x64 Debug TouchMe fieldinit
  2.0.50727.8825 x64 Release
  2.0.50727.8825 x64 Release TouchMe fieldinit

     

.NET 4.7.1

     

4.7.2556.0 x86 Debug
  4.7.2556.0 x86 Debug TouchMe fieldinit
  4.7.2556.0 x86 Release Δ
  4.7.2556.0 x86 Release TouchMe Δ
  4.7.2556.0 x64 Debug
  4.7.2556.0 x64 Debug TouchMe fieldinit
  4.7.2556.0 x64 Release
  4.7.2556.0 x64 Release TouchMe Δ

正如介绍中所述,或许比版本 2.0 / 3.5 4.7 增量相比更有意思 < / em>当前的.NET版本,因为它们表明,虽然现在x86x64之间的字段初始化行为比以前更加一致,但仍然可以体验今天DebugRelease版本之间的运行时字段初始化行为存在显着差异。

语义将取决于你是否碰巧在课堂上调用了一个不相交或看似无关的静态方法,所以如果这样做会给你的整体设计带来一个错误,那么它很可能是非常神秘且难以追踪的

备注
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
}

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

您还可以执行以下操作:             type.TypeInitializer.Invoke(null,null);

答案 6 :(得分:0)

我不确定您的用例是什么,但是您可以使用部分类和内部类强制静态初始化程序以相当hacky的方式运行:

这个想法是使用带有静态字段的局部外部类,该静态字段访问内部类上的另一个静态字段。 当对外部类进行静态初始化时,静态字段初始化将启动所有内部类的静态初始化:


public partial class OutterClass
{
    // When OutterClass is initialized, this will force InnerClass1 to be initialized.
    private static int _innerClass1Touched = InnerClass1.TouchMe;

    public static class InnerClass1
    {
        public static int TouchMe = 0;

        static InnerClass1()
        {
            Console.WriteLine("InnerClassInitialized");
        }
    }
}

public partial class OutterClass
{
    // When OutterClass is initialized, this will force InnerClass2 to be initialized.
    private static int _innerClass2Touched = InnerClass2.TouchMe;

    public static class InnerClass2
    {
        public static int TouchMe = 0;

        static InnerClass2()
        {
            Console.WriteLine("InnerClass2Initialized");
        }
    }
}

然后,在应用程序的早期位置,您只需要以导致静态初始化的方式引用OutterClass,例如构造它的实例即可。

一个更现实的例子可能是...

public interface IService
{
    void SayHello();
}

public partial class ServiceRegistry
{
    private static List<Func<IService>> _serviceFactories;

    private static void RegisterServiceFactory(Func<IService> serviceFactory)
    {
        // This has to be lazily initialized, because the order of static initialization
        //  isn't defined. RegisterServiceFactory could be called before _serviceFactories
        //  is initialized.
        if (_serviceFactories == null)
            _serviceFactories = new List<Func<IService>>();

        _serviceFactories.Add(serviceFactory);
    }

    public List<IService> Services { get; private set; }

    public ServiceRegistry()
    {
        Services = new List<IService>();

        foreach (var serviceFactory in _serviceFactories)
        {
            Services.Add(serviceFactory());
        }
    }
}


// In another file (ServiceOne.cs):
public class ServiceOne : IService
{
    void IService.SayHello()
    {
        Console.WriteLine("Hello from ServiceOne");
    }
}

public partial class ServiceRegistry
{
    // When OutterClass is initialized, this will force InnerClass1 to be initialized.
    private static int _serviceOneRegistryInitializer = ServiceOneRegistry.Initialize;

    private static class ServiceOneRegistry
    {
        public static int Initialize = 0;

        static ServiceOneRegistry()
        {
            ServiceRegistry.RegisterServiceFactory(() => new ServiceOne());
        }
    }
}

// In another file (ServiceTwo.cs):
public class ServiceTwo : IService
{
    void IService.SayHello()
    {
        Console.WriteLine("Hello from ServiceTwo");
    }
}

public partial class ServiceRegistry
{
    // When OutterClass is initialized, this will force InnerClass1 to be initialized.
    private static int _serviceTwoRegistryInitializer = ServiceTwoRegistry.Initialize;

    private static class ServiceTwoRegistry
    {
        public static int Initialize = 0;

        static ServiceTwoRegistry()
        {
            ServiceRegistry.RegisterServiceFactory(() => new ServiceTwo());
        }
    }
}

static void Main(string[] args)
{
    ServiceRegistry registry = new ServiceRegistry();
    foreach (var service in registry.Services)
    {
        serivce.SayHello();
    }

    // Output will be:
    // Hello from ServiceOne
    // Hello from ServiceTwo

    // No guarantee on order.
}

为什么还要这样做?它的用例非常狭窄。它消除了使用单个方法初始化和注册所有服务的需要。 我个人想消除单一方法初始化的情况是出于代码生成的目的。

答案 7 :(得分:-1)

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

答案 8 :(得分:-1)

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

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

修改

静态构造函数在any static members are referenced时运行。您可以简单地创建一个名为initialize的虚方法,该方法除了确保框架调用静态构造函数外什么都不做。

答案 9 :(得分:-3)

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