Как IDisposable работает с использованием и возвратом? - PullRequest
3 голосов
/ 24 мая 2019

В рабочих процессах 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 бросает)?

Ответы [ 2 ]

2 голосов
/ 24 мая 2019

Они будут происходить до того, как значение будет возвращено из выражения вычисления, семантически они будут происходить в блоке finally.Если вы хотите посмотреть на источник сгенерированного оператора using, вы можете найти его здесь .Он эффективно генерирует контролируемую доступом функцию dispose, которая вызывает Dispose() на ресурсе, который вы передаете, затем создает асинхронный блок try-finally с этой функцией в предложении finally.

0 голосов
/ 25 мая 2019

У меня обычно есть два решения.

Первое решение активно захватывает исключение, вручную удаляет одноразовый объект и повторно выдает исключение:

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

Второе решение заключается в использовании функции продолжения, которая будет иметь доступ к одноразовому объекту до того, как асинхронный возврат вернется:

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)
    ...
}
...