2016-12-28 49 views
7

我宣布这样一个元组:F#元组恒从未初始化

module MyModule = 
    let private INVALID_TUPLE = ("0", DateTime.MinValue) 

当我引用它较低的模块中,它总是空:

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

此外,当我把一个元组声明中的断点,它从来没有命中。

如果我做一个脚本(FSX)文件完全一样的东西,开始调试,运行,在元组声明命中断点和参考元组是不错的。

ILSpy我的模块显示,有产生了一些启动代码具有创建INVALID_TUPLE一个主要方法。显然,这是不是由于某种原因运行?

这里是再现行为(现在我知道它是与MSTest的执行代码的方式)的样品。从C#单元测试中调用它;结果将为空。事实上,F#代码中没有任何断点将会执行。通过从库中引用它,或者使用单元测试亚军 - 例如,当您运行编译为在不运行编译可执行文件的Main法的方式进行的可执行代码都可能发生

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 
+0

你怎么知道它是空的?某个地方实际上不起作用吗? –

+0

好的,我得到一个NRE下游,我也知道它是空的,因为调试器告诉我这样。 – dudeNumber4

+0

调试器不可靠。不要依赖它。你能说明你在哪里得到NRE,以及这个所谓的空值是如何到达那里的?或者提供另一个最小可重现的例子?另请参阅“[如何提出一个好问题](http://stackoverflow.com/help/how-to-ask)”。 –

回答

7

错误。解决方案是将F#项目编译为一个库,并可能有另一个可执行文件作为入口点。 (或者,你也可以修改代码以避免let约束全局值,但我更喜欢第一种方法。)

这是由F#编译器的编译为可执行代码和不同的方式处理初始化造成的事实用于编译为库的代码。

  • 对于库,初始化代码被置于静态构造,当该字段被访问的第一次
  • 对于可执行文件,初始化代码被放入Main方法,该方法被执行并且在运行时的应用程序启动(但只有当它作为普通可执行文件启动时)。

我想这样做的原因是,F#编译器试图保持在初始化发生(从上到下)的顺序。对于可执行文件,可以通过运行Main方法中的初始化程序来完成此操作。对于图书馆来说,没有可靠的方法来做到这一点(因为图书馆没有“初始化”),所以使用静态构造函数是次佳选择。