F# 中的静态构造函数 - 它们什么时候 运行?

Static constructors in F# - when do they run?

我正在试验在 F# 中创建单例的各种方法,以便更好地理解其中的微妙之处。我不知道单例模式在 F# 中是否有用,但我想试验一下。我对涉及这些单例实例上的静态构造函数的一个结果感到惊讶。首先,我将向您展示我的代码,然后我将详细介绍我的问题。

在一个名为 TrySingleton 的项目中,我创建了三个模块。这是 Eager.fs:

module TrySingleton.Eager

type EagerClass() =
    do
        printfn "Initializing eager class..."

    static do
        printfn "Static constructor of eager class"

let Instance = EagerClass()

这里是Lazy.fs

module TrySingleton.Lazy

type LazyClass() =
    do
        printfn "Initializing lazy class..."

    static do
        printfn "Static constructor of lazy class"

let Instance = lazy LazyClass()

我是这样称呼它们的,在 Main.fs:

module TrySingleton.Main

[<EntryPoint>]
let main argv =
    printfn "Starting main with args %A" argv
    printfn "Accessing eager instance:"
    printfn "%A" Eager.Instance
    printfn "Accessing lazy instance:"
    printfn "%A" Lazy.Instance.Value
    printfn "Accessing eager instance again:"
    printfn "%A" Eager.Instance
    printfn "Accessing lazy instance again:"
    printfn "%A" Lazy.Instance.Value
    printfn "Success; exiting."
    0

我期待 Eager class 的静态构造函数在程序启动时立即变为 运行,但不确定 Lazy class 的静态构造函数会 运行。但是,这是我得到的输出:

Starting main with args [||]
Accessing eager instance:
Static constructor of eager class
Initializing eager class...
TrySingleton.Eager+EagerClass
Accessing lazy instance:
Static constructor of lazy class
Initializing lazy class...
TrySingleton.Lazy+LazyClass
Accessing eager instance again:
TrySingleton.Eager+EagerClass
Accessing lazy instance again:
TrySingleton.Lazy+LazyClass
Success; exiting.

看来Eagerclass并没有我想象的那么急切。它的静态构造函数只是 运行 我第一次尝试访问该实例时,而我认为静态 class 构造函数会在程序启动时 运行。

我想我没有太多问题了,除了问:这在任何地方都有记录吗?我错过了什么关于 class 的静态构造函数何时为 运行 的文档?

我设法在官方 F# 文档中找到了答案,我很少再看它了,因为官方 F# 文档中的 http://fsharpforfunandprofit.com is such a great resource. But the Constructors article 说(强调我的):

In addition to specifying code for creating objects, static let and do bindings can be authored in class types that execute before the type is first used to perform initialization at the type level.

let Bindings in Classes and do Bindings in Classes 文章有后续链接,其中说(再次强调我的):

Static let bindings are part of the static initializer for the class, which is guaranteed to execute before the type is first used.

A do binding in a class definition performs actions when the object is constructed or, for a static do binding, when the type is first used.

看来我可以回答我自己的问题了:答案是我最初的预期是错误的。静态构造函数在程序启动时 不一定 是 运行,但仅当 首次使用 时才会 class。这意味着如果您在两个不同的 class 中使用单例模式,其中一个依赖于另一个,则它们的构造函数(和静态构造函数)将按照给定的合理顺序 运行的依赖。 (当然,在那种情况下可能有更好、更实用的方法来设计您的代码;我将其用作说明而不是作为对该设计的认可。)