如何执行模块做阻塞?

dev*_*rts 3 f#

我需要在一个模块中进行一些设置,我希望通过利用一个do块来完成.奇怪的是,我的do街区似乎永远不会受到打击.

更奇怪的是,如果我将模块代码加载到fsi中,它确实会受到攻击.这是我的例子:

Main.fs

[<EntryPoint>]
let main args = 
    printfn "%b" TestNamespace.M.x
    0
Run Code Online (Sandbox Code Playgroud)

TestModule.fs

namespace TestNamespace

module M = 
    do
        printfn "In do"
        failwith "Error" // this is line 6

    let x  = true
Run Code Online (Sandbox Code Playgroud)

当我运行编译的可执行文件时,我得到

>test.exe
 true
Run Code Online (Sandbox Code Playgroud)

为什么不抛出异常?如果我自己在FSI中运行模块,我会得到

In do
System.Exception: Error
    at <StartupCode$FSI_0006>.$FSI_0006.main@() in    C:\Projects\Personal2\Playground\fsscripts\fsscripts\TestModule.fs:line 6
Stopped due to error
Run Code Online (Sandbox Code Playgroud)

所以它得到了例外.

我在转换中看到do初始化程序被转换为单独的类

namespace \u003CStartupCode\u0024fsscripts\u003E
{
  internal static class \u0024Library1
  {
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    [CompilerGenerated]
    [DebuggerNonUserCode]
    internal static int init\u0040;

    static \u0024Library1()
    {
      ExtraTopLevelOperators.PrintFormatLine<Unit>((PrintfFormat<Unit, TextWriter, Unit, Unit>) new PrintfFormat<Unit, TextWriter, Unit, Unit, Unit>("In do"));
      Operators.FailWith<Unit>("Error");
      bool x = M.x;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

VS实际的模块代码:

namespace TestNamespace
{
  [CompilationMapping(SourceConstructFlags.Module)]
  public static class M
  {
    public static bool x
    {
      [DebuggerNonUserCode] get
      {
        return true;
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

那么如何确保do块实际执行?

-

编辑,鉴于上面的示例计为一个简单的常量表达式,因此不会产生可观察的初始化,为什么以下也不起作用?

[<EntryPoint>]
let main args = 
    printfn "%d" (TestNamespace.M.x id 1)
    0
Run Code Online (Sandbox Code Playgroud)
namespace TestNamespace

module M = 
    do
        printfn "In do"
        failwith "Error"

    let x f a = f a
Run Code Online (Sandbox Code Playgroud)

这打印1没问题.


在重新阅读Tomas的注释之后进行编辑,因为函数被认为是一个常量表达式.

Tom*_*cek 6

有关问题的详细解释,请参阅此前SO问题的答案.重要的一点说:

首次访问具有可观察初始化的值时,将执行该文件的静态初始化程序

现在,"可观察的初始化"有些棘手的想法,但简单的常量初始化肯定没有可观察的初始化 - 这就是为什么do不执行块的原因.您可以欺骗编译器认为存在一些必要的操作,例如通过添加do ():

module M = 
    do
        printfn "In do"
        failwith "Error" // this is line 6

    let x = (do ()); true
Run Code Online (Sandbox Code Playgroud)