IDisposable 如何与 use 和 return 一起使用?

How does IDisposable work with use and return?

在 F# async 工作流中,我们可以定义应使用 use 关键字清理的资源。

但是 use 如何与 return 互动?

例如,给定此代码:

let createResource = async {
  use r = Resource ()

  do! operationThatMightThrow r

  return r
}

async {
  use! r = createResource

  printfn "%O" r
}
|> Async.RunSynchronously

Resource.Dispose 的调用将在哪里发生?

我该如何设计才能使 r 始终被清理(即使 operationThatMightThrow 抛出)?

它们将在计算表达式返回值之前发生,从语义上讲,它们将发生在 finally 块中。如果想查看生成的using语句的来源,可以找here。它有效地生成一个 access-controlled dispose 函数,该函数在您传入的资源上调用 Dispose(),然后在 finally 子句中使用该函数创建一个异步 try-finally 块。

我通常有两种解决方案。

第一种方案是主动捕获异常,手动处理一次性对象,re-throwing异常:

let createResource = async {
    let r = new Resource ()
    try do! operationThatMightThrow r
    with e -> (r :> IDisposable).Dispose(); raise e
    return r
}

第二种解决方案是使用延续函数,该函数将在异步之前访问一次性对象 returns:

let createResource cont = async {
    use r = new Resource ()
    do! operationThatMightThrow r
    return cont r
}

async {
    let! x = createResource (fun r -> printfn "in cont: %O" r)
    ...
}