似乎有两种方法可以返回错误async
工作流程:raise
and Result
.
let willFailRaise = async {
return raise <| new Exception("oh no!")
}
let willFailResult = async {
return Result.Error "oh no!"
}
对于调用者来说,处理有点不同:
async {
try
let! x = willFailRaise
// ...
with error ->
System.Console.WriteLine(error)
}
async {
let! maybeX = willFailResult
match maybeX with
| Result.Ok x ->
// ...
| Result.Error error ->
System.Console.WriteLine(error)
}
我的问题是:
- 每种方法的优点/缺点是什么?
- 哪种方法更符合 F# 习惯?
这取决于我们谈论的是哪种错误。基本上有3种:
- 域错误(例如,用户提供的数据无效、使用此电子邮件的用户已注册等)
- 基础设施错误(例如,您无法连接到另一个微服务或数据库)
- 恐慌(例如
NullReferenceException
or StackOverflowException
等),这是由于程序员的错误造成的。
虽然这两种方法都可以完成工作,但通常您应该关心的是使您的代码尽可能自记录且易于阅读。这意味着以下内容:
-
域错误:一定要去
Result
。这些“错误”是预料之中的,它们是您工作流程的一部分。使用Result
在函数的签名中反映了您的业务规则,这非常有用。
-
基础设施故障:这取决于。如果你有微服务,那么这些失败可能是预料之中的,也许使用起来会更方便
Result
。如果没有——就寻求例外。
-
Panics:确实
Exception
。首先,你不能覆盖一切 with Result
,无论哪种方式,你都需要全局异常过滤器。第二件事 - 如果你试图覆盖所有可能的恐慌 - 代码会很快变成一场可怕的灾难,这将消除使用的全部意义Result
对于域错误。
所以这确实与Async
或者C#互操作,它是关于代码的可读性和可维护性。
至于 C# iterop ——不用担心,Result
有所有可以提供帮助的方法,例如IsError
等等。但您始终可以添加扩展方法:
[<AutoOpen>]
module Utils =
type Result<'Ok, 'Error> with
member this.Value =
match this with
| Ok v -> v
| Error e -> Exception(e.ToString()) |> raise
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)