“关闭不能捕获可变变量”给了我一个糟糕的一天

时间:2013-06-24 02:33:07

标签: f#

我正在使用extern DLL,它有一堆返回ReturnCode枚举的例程,因此我编写了以下帮助函数来记录所有错误:

let mutable LastError = ReturnCode.OK
let mutable LastReturnCode = ReturnCode.OK
let mutable TotalErrors = 0

let Run (call: unit -> ReturnCode) =
  LastReturnCode <- call()
  if LastReturnCode <> ReturnCode.OK then
    LastError <- LastReturnCode
    TotalErrors <- TotalErrors + 1

很好,除了一些DLL的函数有out个参数。所以现在我做的事情就像

let CreateEvfImageRef (streamHandle: int) =
  let mutable evfImageHandle = 0
  Run (fun () -> Extern.EdsCreateEvfImageRef (streamHandle, &evfImageHandle))
  evfImageHandle

编译器给我一个“可变变量无法通过闭包捕获”的错误。除了在任何地方内联Run之外,我还能做些什么吗?这在C#中运行良好。

(以下示例extern声明)

[<DllImport(EDSDKPath)>]
extern ReturnCode EdsCreateEvfImageRef(int inStreamHandle, [<Out>] int& outEvfImageHandle);

2 个答案:

答案 0 :(得分:6)

您仍然可以使用ref类型,但在传递对函数的引用时不需要编写&符号 - 编译器将自动执行此操作:

let CreateEvfImageRef (streamHandle: int) =
  let mutable evfImageHandle = ref 0
  Run (fun () -> Extern.EdsCreateEvfImageRef (streamHandle, evfImageHandle))
  !evfImageHandle

答案 1 :(得分:3)

标准解决方案是使用引用 - 代码变为

let CreateEvfImageRef (streamHandle: int) =
  let evfImageHandle = ref 0
  Run (fun () -> Extern.EdsCreateEvfImageRef (streamHandle, &(!evfImageHandle)))
  !evfImageHandle

然而,这不起作用,因为编译器要求!evfImageHandle是可变的,而不是。

我认为这里真正的解决方案是更改你的Run函数,使其不需要关闭,而只需要返回值 - 这至少会使它在这种情况下起作用。然后代码变成

let Run (call: ReturnCode) =
  LastReturnCode <- call
  if LastReturnCode <> ReturnCode.OK then
    LastError <- LastReturnCode
    TotalErrors <- TotalErrors + 1

并且代码更改为

let CreateEvfImageRef (streamHandle: int) =
  let mutable evfImageHandle = 0
  Extern.EdsCreateEvfImageRef (streamHandle, &evfImageHandle)) |> Run
  evfImageHandle

或更恶意的解决方案。使用数组成员可变的事实,并且可以通过闭包来捕获

let CreateEvfImageRef (streamHandle: int) =
  let evfImageHandle =  [|0|]
  Run (fun () -> EdsCreateEvfImageRef (streamHandle, &(evfImageHandle.[0])) )
  evfImageHandle.[0]