F#中的模块值未初始化.为什么?

LLS*_*LLS 13 f# module

当我使用F#时,我有一种奇怪的行为.当我在模块中使用let绑定时,如果值是从构造函数创建的,那么在外部使用时它是未初始化的.(我使用ModuleName.s2或ModuleName.f()从C#中使用它)

//in a module
let s1 = "1" //normal
let s2 = new String('i', 5) //null

let f () =
    s2.Equals("something") //Exception
Run Code Online (Sandbox Code Playgroud)

这是正常行为吗?提前致谢.

编辑:为了调试,我选择将其编译为可执行文件.这可能是其他人指出的问题.

kvb*_*kvb 16

在F#库中,模块通过静态构造函数初始化,这些构造函数确保在使用任何模块的值之前进行初始化.相反,在F#可执行文件中,此初始化在应用程序的入口点中执行.这意味着如果另一个程序集引用F#应用程序(无论其他应用程序的语言是什么),都不会运行初始化代码.

UPDATE

Brian向我指出了规范的这一部分,这表明这是预期的行为.

看起来一种解决方法是提供一个明确的入口点,如下所示:

[<EntryPoint>]
let main _ =
    0
Run Code Online (Sandbox Code Playgroud)

然后,您可以从C#app调用此main方法,以确保正确初始化模块的内容.

更新2

我误读了规范 - 你不需要实际调用引用程序集中的显式入口点.它的存在只会导致初始化正确发生.


svi*_*ick 5

出于某种原因,SomeModule.s2将其实现为(只读)属性,该属性返回unspeakable静态字段的值<StartupCode$FS>.$Program.s2@9.如果编译为应用程序,则在main方法中初始化该字段.从C#代码中使用时,不会调用此方法,因此不会初始化该字段.

如果编译为库,代码是相同的,除了字段在类的静态构造函数中初始化$Program,因此它在从C#使用时应该工作.

s1总是有效的原因是优化:F#编译器理解它是一个常量并实现f()"1".Equals("something").