たまたまnullになるネストされたAsyncを呼び出すときに、問題に遭遇しました。例外が発生しましたが、非同期ワークフローが提供する通常の例外処理方法ではキャッチできません。
以下は、問題を再現する簡単なテストです。
[<Test>]
let ``Nested async is null with try-with``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
try
do! g()
with e ->
printf "%A" e
}
f |> Async.RunSynchronously |> ignore
次の例外が発生します。
System.NullReferenceException : Object reference not set to an instance of an object.
at Microsoft.FSharp.Control.AsyncBuilderImpl.bindA@714.Invoke(AsyncParams`1 args)
at <StartupCode$FSharp-Core>.$Control.loop@413-40(Trampoline this, FSharpFunc`2 action)
at Microsoft.FSharp.Control.Trampoline.ExecuteAction(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.TrampolineHolder.Protect(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.AsyncBuilderImpl.startAsync(CancellationToken cancellationToken, FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at Microsoft.FSharp.Control.CancellationTokenOps.starter@1121-1.Invoke(CancellationToken cancellationToken, FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously(CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously(FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
at Prioinfo.Urkund.DocCheck3.Core2.Tests.AsyncTests.Nested async is null with try-with() in SystemTests.fs: line 345
この場合、例外をキャッチする必要があると本当に思いますか、それともこれは本当に期待される動作ですか? (記録には Visual Studio 2010 Sp1 を使用しています)
また、次のテスト ケースAsync.Catch
でAsync.StartWithContinuations
示されているのと同じ問題が発生します。
[<Test>]
let ``Nested async is null with Async.Catch``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
do! g()
}
f |> Async.Catch |> Async.RunSynchronously |> ignore
[<Test>]
let ``Nested async is null with StartWithContinuations``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
do! g()
}
Async.StartWithContinuations(f
, fun _ -> ()
, fun e -> printfn "%A" e
, fun _ -> ())
ワークフロービルダーのバインドメソッド内で例外が発生したようで、結果として通常のエラー処理コードがバイパスされていると思います。ドキュメントや他の場所で、これが意図された動作であることを示唆するものを見つけられなかったため、非同期ワークフローの実装のバグのように見えます。
ほとんどの場合、回避するのはかなり簡単だと思うので、少なくとも私にとっては大きな問題ではありませんが、すべての例外をキャプチャできる非同期例外処理メカニズムを完全に信頼できないことを意味するため、少し不安です.
編集:
少し考えた後、kvbに同意します。Null非同期は通常のコードには実際には存在すべきではなく、おそらくすべきではないことを行う場合(Unchecked.defaultOfを使用するなど)、またはリフレクションを使用して値を生成する場合にのみ生成される可能性があります(私の場合、それは関連するモックフレームワークでした) . したがって、これは実際にはバグではなく、エッジ ケースです。