49

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

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

编辑:

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

10 回答 10

121

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

Type type = ...;
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
于 2010-04-16T16:42:53.537 回答
17

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

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();
于 2010-04-16T15:36:01.980 回答
6

每当发生以下情况之一时,都会调用 cctor(静态构造函数);

  1. 你创建一个类的实例
  2. 访问任何静态成员
  3. 在此之前的任何时间,如果BeforeFieldInit已设置

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

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

于 2010-04-16T15:40:39.790 回答
3

扩展 Fábio 的观察,以下简短而完整的测试程序公开了对 JIT 敏感的TypeAttributes.BeforeFieldInit行为细节,将.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.54.7版本更有趣的是当前 .NET 版本的差异x86,因为它们表明,尽管现在的字段初始化行为x64比过去更加一致,您和今天的构建之间的运行时字段初始化行为仍然可能存在显着差异。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
}
于 2017-12-22T01:27:00.403 回答
3

你也可以这样做:

type.TypeInitializer.Invoke(null, null);
于 2018-08-09T04:15:02.780 回答
2

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

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

于 2016-12-30T15:43:36.047 回答
-1

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

于 2010-04-16T15:15:20.513 回答
-1

正如其他人所说,静态构造函数自动运行。如果您需要显式,也许您应该将其重构为可以显式运行的静态方法?

当然,显式调用静态方法也会确保静态构造函数已被执行。

编辑

静态构造函数在引用任何静态成员时运行。您可以简单地创建一个名为的虚拟方法,该方法initialize除了确保框架调用静态构造函数之外什么都不做。

于 2010-04-16T15:29:05.860 回答
-1

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

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


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

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

于 2019-04-02T16:13:46.997 回答
-3

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

于 2010-04-16T15:12:03.873 回答