简体   繁体   English

如何运行静态构造函数?

[英]How can I run a static constructor?

I'd like to execute the static constructor of a class (ie I want to "load" the class) without creating an instance.我想在不创建实例的情况下执行类的静态构造函数(即我想“加载”类)。 How do I do that?我怎么做?

Bonus question: Are there any differences between .NET 4 and older versions?额外问题:.NET 4 和旧版本之间有什么区别吗?

Edit:编辑:

  • The class is not static.该类不是静态的。
  • I want to run it before creating instances because it takes a while to run, and I'd like to avoid this delay at first access.我想在创建实例之前运行它,因为它需要一段时间才能运行,我想在第一次访问时避免这种延迟。
  • The static ctor initializes private static readonly fields thus cannot be run in a method instead.静态构造函数初始化private static readonly字段,因此不能在方法中运行。

The other answers are excellent, but if you need to force a class constructor to run without having a reference to the type (ie reflection), you can use RunClassConstructor :其他答案非常好,但是如果您需要强制运行类构造函数而不引用类型(即反射),则可以使用RunClassConstructor

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

Just reference one of your static fields.只需引用您的静态字段之一。 This will force your static initialization code to run.这将强制您的静态初始化代码运行。 For example:例如:

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

Usage:用法:

// initialize statics
MyClass.TouchMe();

The cctor (static constructor) will be called whenever one of the following occurs;每当发生以下情况之一时,都会调用 cctor(静态构造函数);

  1. You create an instance of the class你创建一个类的实例
  2. Any static member is accessed访问任何静态成员
  3. Any time before that, if BeforeFieldInit is set在此之前的任何时间,如果BeforeFieldInit被设置

If you want to explicitly invoke the cctor, assuming you have other static members, just invoke/access them.如果您想显式调用 cctor,假设您有其他静态成员,只需调用/访问它们。

If you are not doing anything very interesting in your cctor, the compiler may decide to mark it BeforeFieldInit , which will allow the CLR the option to execute the cctor early.如果您没有在 cctor 中做任何非常有趣的事情,编译器可能会决定将其标记为BeforeFieldInit ,这将允许 CLR 选择提前执行 cctor。 This is explained in more detail here: http://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx这里有更详细的解释: http : //blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx

Extending Fábio's observations , the following short and complete test program exposes the JIT-sensitive details of TypeAttributes.BeforeFieldInit behavior, comparing .NET 3.5 to the latest version (as of late 2017) .NET 4.7.1 , and also demonstrates the potential hazards for build type variations within each version itself.扩展 Fábio 的观察,以下简短而完整的测试程序公开了TypeAttributes.BeforeFieldInit行为的 JIT 敏感细节,将.NET 3.5与最新版本(截至 2017 年末) .NET 4.7.1 进行了比较,并展示了在每个版本本身内构建类型变体。 [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);
    }
};

Below is the console output from running this program in all combinations of { x86, x64 } and { Debug, Release } .以下是在{ x86, x64 }{ Debug, Release } 的所有组合中运行此程序的控制台输出。 I manually added a delta symbol Δ (not emitted by the program) to highlight the differences between the two .NET versions.我手动添加了一个增量符号Δ (不是由程序发出)来突出两个 .NET 版本之间的差异。

.NET 2.0/3.5 .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 .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 Δ

As noted in the intro, perhaps more interesting than the version 2.0 / 3.5 versus 4.7 deltas are the differences within the current .NET version, since they show that, although the field-initialization behavior nowadays is more consistent between x86 and x64 than it used to be, it's still possible to experience a significant difference in runtime field initialization behavior between your Debug and Release builds today.正如在介绍指出,也许比版本更有趣的2.0 / 3.54.7三角洲是目前的.NET版本的不同,因为它们表明,虽然场初始化时下的行为之间是比较一致x86x64比以前尽管如此,今天仍然有可能在您的DebugRelease版本之间体验到运行时字段初始化行为的显着差异

The semantics will depend on whether you happen to call a disjoint or seemingly unrelated static method on the class or not, so if doing so introduces a bug for your overall design, it is likely to be quite mysterious and difficult to track down.语义将取决于您是否碰巧在类上调用了不相交或看似无关的静态方法,因此如果这样做会为您的整体设计引入错误,则可能会非常神秘且难以追踪。


Notes笔记
1. The above program uses the following utility function to display the current CLR version: 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
}

你也可以这样做:

type.TypeInitializer.Invoke(null, null);

Static constructors are NOT always called when accessing a static method!访问静态方法时并不总是调用静态构造函数!

I noticed that if you call a static method in a base class, the static constructor of the super class is NOT called.我注意到,如果您在基类中调用静态方法,则不会调用超类的静态构造函数。 This unexpected behavior has bitten many times.这种意想不到的行为已经被咬了很多次。

There is no need to do this, the whole point of a static constructor is that it runs once when the class is first initialised at first access.没有必要这样做, 静态构造函数的全部意义在于它在第一次访问时首次初始化类时运行一次。 If you want to run something on demand, then consider adding your initialisation code into a public method that is called by the constructor.如果您想按需运行某些东西,请考虑将您的初始化代码添加到由构造函数调用的公共方法中。 You can then call this method whenever you like.然后,您可以随时调用此方法。 But I'm not sure why you would want to do this?但我不确定你为什么要这样做?

As others have said, static constructors run automatically.正如其他人所说,静态构造函数会自动运行。 If you need to be explicit, maybe you should refactor it into a static method which you can run explicitly?如果您需要显式,也许您应该将其重构为可以显式运行的静态方法?

Explicitly calling a static method would also, of course, ensure that the static constructor had been executed.当然,显式调用静态方法还可以确保静态构造函数已被执行。

edit编辑

Static constructors are run when any static members are referenced .引用任何静态成员时,将运行静态构造函数。 You could simply create a dummy method called initialize which did nothing but ensure that the framework calls the static constructor.您可以简单地创建一个名为initialize的虚拟方法,它除了确保框架调用静态构造函数之外什么都不做。

I'm not exactly sure what your use case is for this, but you can force static initializers to run in a fairly hacky way using partial classes and inner classes:我不确定您的用例是什么,但是您可以使用部分类和内部类强制静态初始化程序以相当hacky的方式运行:

The idea is to use a partial outter class with a static field that accesses another static field on the inner class.这个想法是使用带有静态字段的部分外部类,该静态字段访问内部类上的另一个静态字段。 When the outter class is statically initialized, the static field initialization will kick-off the static initialization of all the inner classes:当外部类静态初始化时,静态字段初始化将启动所有内部类的静态初始化:


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

Then, somewhere early in your application, you just need to reference OutterClass in a way that will result in static initialization, like constructing an instance of it.然后,在应用程序早期的某个地方,您只需要以一种会导致静态初始化的方式引用 OutterClass,例如构造它的一个实例。

A more realistic example might be...一个更现实的例子可能是......

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

Why even do this?为什么还要这样做? It has a very narrow use-case.它有一个非常狭窄的用例。 It eliminates the need to have a single method that initializes and registers all the services.它消除了对初始化和注册所有服务的单一方法的需要。 The case in which I personally want to eliminate that single method initialization is for code generation purposes.我个人想要消除单个方法初始化的情况是为了代码生成目的。

The static constructor runs automatically the first time you access the class.静态构造函数在您第一次访问类时自动运行。 There is no need (or capability) to 'run' it yourself.没有必要(或能力)自己“运行”它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM