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

Возможный дубликат:
Как вызывать статический конструктор с отражением?

У меня есть код инициализации в статическом конструкторе различных классов. Я не могу создавать экземпляры и не знаю заранее типы. Я хотел бы убедиться, что классы загружены.

Я пробовал это:

fooType.TypeInitializer.Invoke (new object[0]);

Но получил MemberAccessException: инициализатор типа не вызывался.

Я предполагаю, что это потому, что cctor является частным? Есть ли способ исправить это, не меняя архитектуру?

Редактировать: я нашел обходной путь, используя RuntimeHelpers.RunClassConstructor, но этот способ, кажется, едва задокументирован в MSDN, и я не уверен, является ли это взломом или разумной системой, подобной системе.


person mafu    schedule 20.12.2010    source источник
comment
Не обман: этот вопрос также касается исключения и того, как его избежать, а также целесообразности использования RunClassConstructor вообще.   -  person mafu    schedule 20.12.2010
comment
@mafuctr: Вы пробовали fooType.TypeInitializer.Invoke(null, null);, как указано в этом ответе?   -  person Ani    schedule 20.12.2010
comment
Почему автоматический вызов статического конструктора вашего класса, сделанный фреймворком, у вас не работает?   -  person Doc Brown    schedule 20.12.2010
comment
@Ani: Это работает, но у меня есть несколько вопросов по этому поводу...   -  person mafu    schedule 20.12.2010


Ответы (2)


Я не уверен, почему это работает, но, насколько я понимаю (с помощью Skeet), если у меня есть статический класс

public static class Statics1
{
    public static string Value1 { get; set; }

    static Statics1()
    {
        Console.WriteLine("Statics1 cctor");
        Value1 = "Initialized 1";
    }
}

Код:

Type staticType = typeof (Statics1);
staticType.TypeInitializer.Invoke(null);
or
staticType.TypeInitializer.Invoke(new object[0]);

вызовет исключение, потому что каким-то образом это разрешается в .ctor вместо .cctor класса.
Если я использую явно статический класс, он обрабатывается как абстрактный запечатанный класс, поэтому исключение состоит в том, что абстрактный класс не может быть создан, и если я использую обычный класс со статическим конструктором, исключение состоит в том, что инициализатор типа не вызывается.

Но если я использую перегрузку Invoke с двумя параметрами (instance, params), вот так:

Type staticType = typeof (Statics1);
staticType.TypeInitializer.Invoke(null, null);

явно заявляя, что я вызываю статический метод (в этом смысл первого нуля - нет экземпляра == static), это работает и инициализирует класс.


Тем не менее, статические конструкторы — странные звери. Вызов одного из них таким образом вызовет статический конструктор, даже если он уже был выполнен, т. е. этот код:

Console.WriteLine(Statics1.Value1);

Type staticType = typeof (Statics1);
staticType.TypeInitializer.Invoke(null, null);

вызовет статический конструктор дважды. Поэтому, если ваши cctors имеют потенциально важные побочные эффекты, такие как создание файлов, открытие баз данных и т. д., вы можете пересмотреть этот подход.

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

person SWeko    schedule 20.12.2010
comment
Превосходно! Это отвечает на большинство вопросов, которые я собирался задать после комментария Ани к ОП. - person mafu; 20.12.2010
comment
Что касается последней части (дважды вызванной), есть ли способ избежать этого? А как насчет stackoverflow.com/questions/2658561/ ... Не могли бы вы добавить это в качестве ответа, так как похоже, что он ведет себя совершенно по-разному для Invoke (null) и Invoke (null, нулевой)? - person mafu; 20.12.2010
comment
Далее: social.msdn.microsoft. com/Forums/en/csharplanguage/thread/ - person mafu; 20.12.2010
comment
На самом деле, я бы рекомендовал использовать RuntimeHelpers.RunClassConstructor, даже если он немного более низкоуровневый и потенциально зависит от версии. - person SWeko; 20.12.2010

РЕДАКТИРОВАТЬ: явно не будет работать как есть, поскольку постеры не могут знать типы заранее. Оставив здесь, как может помочь разъяснение для будущих читателей ...

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

public static class MyStatic
{
    public static string SomeToolMethod()
    {
        return "Hello";
    }
}

public class MyStaticWrapper // or proxy, technically?
{
    public static string SomeToolMethod()
    {
        return MyStatic.SomeToolMethod();
    }
}

fooType.TypeInitializer.Invoke(new MyStaticWrapper()); /// untested, not sure of syntax here
person Tim Barrass    schedule 20.12.2010
comment
Я не совсем понимаю, не могли бы вы добавить короткий пример? - person mafu; 20.12.2010
comment
Я боюсь, что это не удастся, потому что мне нужно знать все типы заранее, чтобы я мог вызывать их статические методы в классе-оболочке. - person mafu; 20.12.2010
comment
Хорошо, ура, оставлю здесь как ответ на случай, если это поможет будущим читателям, добавил предупреждение в заголовок ... - person Tim Barrass; 20.12.2010