【问题标题】:Evaluating static constructors of modules with reflection使用反射评估模块的静态构造函数
【发布时间】: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


【解决方案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)

Here 是更多示例

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    相关资源
    最近更新 更多