Как запустить статический конструктор?

Я хотел бы выполнить статический конструктор класса (т. е. я хочу "загрузить" класс) без создания экземпляра. Как мне это сделать?

бонусный вопрос: есть ли различия между .NET 4 и более старыми версиями?

изменить:

  • класс не является статическим.
  • Я хочу запустить его перед созданием экземпляров, потому что для запуска требуется некоторое время, и я хотел бы избежать этой задержки при первом доступе.
  • статический ctor инициализирует private static readonly поля, таким образом, не могут быть запущены в методе.

9 ответов


другие ответы превосходны, но если вам нужно заставить конструктор класса работать без ссылки на тип (т. е. отражение), вы можете использовать:

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

просто ссылайтесь на одно из ваших статических полей. Это заставит ваш статический код инициализации работать. Например:

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

в cctor (статический конструктор) будет вызываться всякий раз, когда происходит одно из следующих событий;

  1. вы создаете экземпляр класса
  2. доступ к любому статическому члену
  3. в любое время до этого, если BeforeFieldInit установлен

Если вы хотите явно вызвать cctor, предполагая, что у вас есть другие статические члены, просто вызовите/получите к ним доступ.

Если вы не делаете ничего очень интересного в вашем cctor, в компилятор может решить пометить его BeforeFieldInit, что позволит CLR возможность выполнить cctor раньше. Это объясняется более подробно здесь: http://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx


расширения замечания, следующая короткая и полная тестовая программа предоставляет 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);
    }
};

ниже выхода консоли от запуска этой программы во всех комбинации { х86, х64 } и { Debug, Release }. Я вручную добавил Дельта-символ Δ (не испускается программой), чтобы выделить различия между двумя версиями .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 дельты различия внутри текущая версия .NET, так как они показывают, что, хотя поведение инициализации поля в настоящее время более последовательно между x86 и x64 чем это было раньше, это все еще возможно испытать значительную разницу в поведении инициализации поля времени выполнения между Debug и Release строит сегодня.

семантика будет зависеть от того, вызовете ли вы непересекающийся или, казалось бы, несвязанный статический метод в классе или нет, поэтому, если это приведет к ошибке для вашего общего дизайна, это, вероятно, будет довольно загадочным и трудно отследить.


Примечания
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
}

статические конструкторы не всегда вызываются при доступе к статическому методу!

Я заметил, что если вы вызываете статический метод в базовом классе, статический конструктор класса не вызывается. Это неожиданное поведение кусало много раз.


также вы можете сделать это: тип.TypeInitializer.Invoke (null, null);


нет необходимости делать это, весь смысл статический конструктор это то, что он запускается один раз, когда класс сначала инициализируется при первом доступе. Если вы хотите запустить что-то по требованию, подумайте о добавлении кода инициализации в открытый метод, вызываемый конструктором. Затем вы можете вызвать этот метод, когда захотите. Но я не уверен почему вы хотели бы сделать это?


как говорили другие, статические конструкторы запускаются автоматически. Если вам нужно быть явным, может быть, вы должны переделать его в статический метод, который можно явно запустить?

явный вызов статического метода также, конечно, гарантирует, что статический конструктор был выполнен.

редактировать

статические конструкторы запускаются, когда любые статические члены ссылаются. Вы можете просто создать фиктивный метод под названием initialize который ничего не сделал, но гарантировал, что платформа вызывает статический конструктор.


статический конструктор запускается автоматически при первом доступе к классу. Нет необходимости (или возможности) "запускать" его самостоятельно.