【发布时间】:2019-03-14 19:44:27
【问题描述】:
我有很多模块,在启动程序时应该将某些内容添加到更高级别模块中的单个字典中。但是,在编译到控制台应用程序时,模块中的表达式和常量似乎被打包到静态构造函数中,因此除非明确引用/当程序认为需要它们时,否则不会对它们进行评估。
这里有一些关于初始化模块的问题,并且一致认为不可能强制。但是,我还没有看到他们中的任何一个在这方面进行过反思。在 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)
但这似乎无济于事。有没有可能做到这一点?
【问题讨论】:
-
顺便说一句——如果你已经在使用反射来调用它们,你不能把初始化代码放在一个普通的静态方法中吗? (这可能更容易通过反射调用......)
-
是的,你是对的,这就是我目前正在做的事情。这些模块的初始化代码只是放在函数中(我附加了自定义属性而不是模块),这些函数只接受一个单元参数并通过动态方法调用来调用。我唯一遇到的问题是它会在我忘记添加 () 参数以使它们起作用而不是常量的地方失败。我只是希望这种类型初始化方法能够工作,本着“如果它编译,它可能是正确的”的精神。
-
你正在使用
Seq.tryFind,而Seqs 被懒惰地评估——这可能是问题吗? -
我不这么认为;该管道确实成功找到了模块类型。但是,最终的 lambda 函数无法初始化它们的静态构造函数(我什至不知道这是否一定是可能的)。
标签: f# system.reflection static-constructor