F#Tuple Constant永远不会初始化

dud*_*er4 7 f# program-entry-point tuples libraries

我已经宣布了这样一个元组:

module MyModule =
  let private INVALID_TUPLE = ("0", DateTime.MinValue)
Run Code Online (Sandbox Code Playgroud)

当我在模块中引用它时,它总是为null:

let private invalidForNone someOtherTuple =
  match someOtherTuple with
  | None -> INVALID_TUPLE  // it's null
  | Some(t) -> t
Run Code Online (Sandbox Code Playgroud)

此外,当我在元组声明上放置一个断点时,它永远不会命中.

如果我在脚本(fsx)文件中执行完全相同的操作,启动调试,执行,元组声明命中的断点以及对元组的引用是好的.

ILSpy for my module显示生成了一些启动代码,其中包含一个创建INVALID_TUPLE的Main方法.显然,这不是出于某种原因运行?

这是一个重现行为的示例(现在我意识到它与MSTest执行代码的方式有关).从C#单元测试中调用它; 结果将为null.实际上,F#代码中的断点根本不会执行.

module NullTupleTest
open System

let private INVALID_TUPLE = ("invalid", DateTime.MinValue)

let private TupleTest someTuple =
  match someTuple with
  | None -> INVALID_TUPLE
  | Some(dt) -> dt

let Main = TupleTest None
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 7

当您以不运行Main已编译可执行文件的方法的方式运行编译为可执行文件的代码时,可能会发生此错误- 例如,通过从库中引用它或使用单元测试运行器.解决方案是将F#项目编译为库,并且可能还有另一个可执行文件作为入口点.(或者,您也可以修改代码以避免let绑定全局值,但我更喜欢第一种方法.)

这是因为F#编译器对编译为可执行文件的代码和编译为库的代码的初始化方式不同.

  • 对于库,初始化代码放在静态构造函数中,该构造函数在第一次访问该字段时执行
  • 对于可执行文件,初始化代码放在Main方法中,它在应用程序启动时运行(但仅在它作为普通可执行文件启动时运行).

我认为这是因为F#编译器试图保持初始化发生的顺序(从上到下).对于可执行文件,可以通过在Main方法中运行初始化程序来完成.对于库,没有可靠的方法(因为库没有"初始化"),所以使用静态构造函数是下一个最好的选择.