首页 > 解决方案 > 使用反射评估模块的静态构造函数

问题描述

我有很多模块,在启动程序时应该将某些内容添加到更高级别模块中的单个字典中。但是,在编译到控制台应用程序时,模块中的表达式和常量似乎被打包到静态构造函数中,因此除非明确引用/当程序认为需要它们时,否则不会对它们进行评估。

这里有一些关于初始化模块的问题,并且一致认为不可能强制。但是,我还没有看到他们中的任何一个在这方面进行过反思。在 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:对象引用未设置为对象的实例。

我也尝试用这个交换最终的 lambda 函数:

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

但这似乎无济于事。有可能实现这一目标吗?

标签: f#system.reflectionstatic-constructor

解决方案


使用 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)

这里还有一些例子


推荐阅读