如何编写一个 fprintfn 函数,在每次写入时打开、追加、关闭文件,而不会出现 ObjectDisposedException?

How can I write an fprintfn function that opens, appends, closes the file on each write, without getting an ObjectDisposedException?

TL;DR:如何编写类似 fprintfn 的函数来访问和关闭文件,其行为类似于 printf 系列函数,但不会抛出 ObjectDisposedException关于多个参数?


作为一个方便的嵌入函数,我发现自己写了下面的代码,运行了一段时间:

let fprintfn a b = 
    use handle = File.AppendText("somelogfile.log")
    printfn a b |> ignore   // write to stdout
    fprintfn handle a b     // write to logfile

// works
fprintfn "Here we are, %i years of age" 42

// throws ObjectDisposedException
fprintfn "Here we are, %i years of age in $i" 42 DateTime.Now.Year

无论我使用 use 还是 using 并且参数数量超过 2 时都会引发错误。

经过一番摸索之后,我得出结论,只要我将上面的新 fprintfn 函数与多个打印格式参数一起使用,它就会创建一个闭包。这在某种程度上似乎是有意义的,尽管在你 return 明确关闭的情况下更隐蔽(即 return 访问 handle 的实际 fun x -> something变量)。

现在的问题是:如何重写上述语句,同时保留原始 fprintfn 函数使用和语法的便利性,而不让它抛出 ObjectDisposedException?


PS:编写上述函数的首选方法是使用以下方法,它允许所有 fprintfn 语法,但是当您使用单个打印时,它会抛出相同的异常格式参数。

let fprintfn a = 
    use handle = File.AppendText("somelogfile.log")
    printfn a |> ignore
    fprintfn handle a

fprintf "Test"              // works
fprintf "Test: %i" 42       // throws ODE

你是对的 fprintf 创建了一个延续并 returns 它,所以当你调用那个延续时,文件已经关闭了。

但您可以更深入地使用 kprintf。它允许您提供 a "continuation" - 即接收格式化字符串并对其执行任何操作的函数。

let fprintfn a = 
  let doPrint s = 
    use handle = File.AppendText("somelogfile.log")
    printfn "%s" s
    fprintfn handle "%s" s

  kprintf doPrint a

然后,当然,您可以使用 File.AppendAllText:

稍微简化一下
let fprintfn a = 
  let doPrint s = 
    File.AppendAllText("somelogfile.log", s + "\n")
    printfn "%s" s
  kprintf doPrint a