Оценка статических конструкторов модулей с отражением

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

Здесь было несколько вопросов относительно инициализации модулей, и все согласны с тем, что это невозможно принудительно. Однако я не видел, чтобы кто-либо из них исследовал рефлексию в этом отношении. Я знаю, что в C# вы можете вызывать статический конструктор типа, поэтому я попытался сделать то же самое с модулями F#.

Мои попытки включали добавление настраиваемого атрибута (MessageHandlerAttribute) в каждый модуль, содержащий такое выражение, которое я хочу оценить при запуске программы, а затем запустить это:

let initAllMessageHandlerModules =
    Assembly.GetExecutingAssembly().GetTypes() 
    |> Array.choose (fun typ -> 
        typ.CustomAttributes 
        |> Seq.tryFind (fun attr -> attr.AttributeType = typeof<MessageHandlerAttribute>)
        |> Option.map (fun _ -> typ))
    |> Array.iter 
        (fun typ -> try typ.TypeInitializer.Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)

Но это дает мне следующую ошибку: System.NullReferenceException: ссылка на объект не указывает на экземпляр объекта.

Я также попытался поменять последнюю лямбда-функцию следующим образом:

(fun typ -> try System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typ.TypeHandle) |> ignore with | ex -> printfn "%A" ex)

Но это, кажется, ничего не делает. Можно ли этого добиться?


person FPguy    schedule 14.03.2019    source источник
comment
Кстати, если вы уже используете отражение для их вызова, не могли бы вы вместо этого поместить код инициализации в обычный статический метод? (Это может быть проще вызвать через отражение...)   -  person Tomas Petricek    schedule 15.03.2019
comment
Да, вы правы, это то, что я сейчас делаю, чтобы заставить его работать. Код инициализации этих модулей просто помещается в функции (к которым я прикрепляю пользовательский атрибут вместо модуля), которые принимают только единичный аргумент и вызываются посредством вызова динамического метода. Единственная проблема, с которой я столкнулся, заключается в том, что в тех местах, где я забываю добавить аргумент (), чтобы сделать их функциями, а не константами, произойдет сбой. Я просто надеялся, что этот подход к инициализации типа сработает, в духе того, что если он скомпилируется, он, вероятно, будет правильным.   -  person FPguy    schedule 15.03.2019
comment
Вы используете Seq.tryFind, а Seq оцениваются лениво - может быть проблема в этом?   -  person LSM07    schedule 16.03.2019
comment
я так не думаю; типы модулей действительно успешно найдены этим конвейером. Однако финальная лямбда-функция не может инициализировать свои статические конструкторы (что, я даже не знаю, обязательно возможно).   -  person FPguy    schedule 16.03.2019


Ответы (1)


Используйте type.GetConstructor вместо type.TypeInitializer.

(fun typ -> try typ.GetConstructor(BindingFlags.Instance ||| BindingFlags.Public, null, CallingConventions.Standard, Array.empty<Type>, Array.empty<ParameterModifier>).Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)

Вот еще несколько примеров

person Bohdan Stupak    schedule 03.04.2019