与 ref 传递的参数互操作

Interop with parameters passed by ref

我正在尝试调用现有库的 C# 函数(我没有时间将整个库移植到 F#)

namespace ExportLib
{

    public static class Xlsx
    {
        public static bool TestSave(string proposed, ref string filename, ref string save_log) {

来自 F# 代码

let getUserFile(proposed) : UserFile =
   let mutable filename = ""
   let mutable save_log = ""
   match Xlsx.TestSave(proposed, ref filename, ref save_log) with
   | true -> FileResult(filename)
   | false -> ErrorMsg(save_log)

意味着将函数转换为具有objective的代数数据类型,以构成非法状态unrepresentable

type UserFile =
  // The value here is the file path.
| FileResult of string
  // The value here is the error msg.
| ErrorMsg of string

我的问题是 mutable F# filename 尽管已在 C# 函数中分配,但仍保持不变(与 out string 而不是 ref string 的问题相同)

在 F# 中,ref 不是关键字,而是创建引用单元格的函数。 Xlsx.TestSave(proposed, ref filename, ref save_log) 因此将两个新创建的 reference 单元格(指向 mutable string 值)传递给 TestSave,这又将 ref 单元格更改为指向任何 string 已分配。不幸的是,这无法从外部观察到,因为没有任何东西指向 ref 单元格。一种方法是:

let getUserFile(proposed) : UserFile =
   let filename = ref ""
   let save_log = ref ""
   match Xlsx.TestSave(proposed, filename, save_log) with
   | true -> FileResult(!filename)
   | false -> ErrorMsg(!save_log)

如@kvb 所述,您还可以使用

let getUserFile(proposed) : UserFile =
   let mutable filename = ""
   let mutable save_log = ""
   match Xlsx.TestSave(proposed, &filename, &save_log) with
   | true -> FileResult(filename)
   | false -> ErrorMsg(save_log)

并从 F# 4.0 simplified the use of mutables vs. ref.

开始完全删除 ref

此外,我尽量避免在简单的 bool 上使用 match,传统的 if then else 更短:

let getUserFile(proposed) : UserFile =
   let mutable filename = ""
   let mutable save_log = ""
   if Xlsx.TestSave(proposed, &filename, &save_log) then
       FileResult(filename)
   else
       ErrorMsg(save_log)

显然, 更好,但是对于“...一个已经存在的库...”,您可能没有那个选择。

如果您在 C# 端使用 out 而不是 ref,那么您应该可以这样做:

let getUserFile(proposed) : UserFile =
    match Xlsx.TestSave proposed with
    | true, filename, _  -> FileResult(filename)
    | false, _, save_log -> ErrorMsg(save_log)

因为可以将尾随 out 参数视为具有实际结果类型的元组。