何时在F#中引发nullArg异常

3

来自Exceptions(抛出和捕获的语法),它说F#内置了四个有用的异常关键字,其中nullArg会抛出NullArgumentException

let f x =
   if x then "ok"
   else nullArg "paramName" "message"

如果我只为 F# 编写模块,那么什么时候应该引发 NullArgumentException

2个回答

4
即使你只是为了F#使用而编写代码,我认为在F#代码中使用异常有完全有效的情况。
如果您预期会出现错误,则最好使用Result类型。这包括读取可能存在格式不正确(因为它来自用户)或验证可能不正确的用户输入数据。
但是,我认为异常仍然对于指示异常情况很有用。对于nullArg特别地,由于F#大部分消除null值,所以你并不需要太多情况,但是当您将.NET类型作为参数时,仍然可能遇到它。如果您有一个函数接受IDictionary<string, string>,并且您从不希望它为null,可以编写:
let lookupName (dict:IDictionary<string, string>) = 
  if dict = null then nullArg "dict" "lookupName expects a valid dictionary!"
  if dict.ContainsKey "name" then dict.["name"] else "anonymous"

作为.NET类型,F#不保证IDictionary永远不会为null。如果在代码中不小心出错,明确处理这种情况可能会提供更有用的错误信息。

2
@MiP - Tomas通常给出非常好的建议。然而,在这种情况下,他提供的建议是针对那些相对熟悉F#的人,而你似乎还处于学习阶段。因此,我这一次不同意Tomas的看法。:-) 在你学习F#的这个阶段,你应该完全避免使用异常,并采用Fyodor Soikin推荐的Result类型。一旦你学会了F#,并且知道何时使用异常比Result类型更有用(这很少见,但偶尔会发生),那么只有在那时你才应该遵循Tomas的建议。 - rmunn
1
@rmunn 嗯,我支持Tomas的观点。我知道在F#世界中,这个异常话题一直备受争议,但我认为它其实很简单。如果成功或失败是你的API的一部分,一个简单的例子就是将字符串解析为整数,那么Result显然是最好的选择,但在Tomas描述的空参数情况下,返回Result没有任何好处,只会污染你的API。你只想要一个有用的错误信息来使调用者修复他们的代码。 - TheInnerLight
@theinnerlight 如果你正在编写一个F#专属的API(正如OP所指示的那样),则无需使用可空类型,因此也没有抛出nullArg的场合。但是如果你处于一般的.NET领域(这里并不是这种情况),那么请按照罗马人的做法去做。 - Fyodor Soikin
1
这个问题特别涉及到 nullArg,它非常少用 - 我选择 IDictionary 只是作为一个非常有用的 .NET 类型的例子。更一般地说,如果“无效结果”不是您预期的公共 API 的正常部分,那么在所有类型中污染 Result 是一个错误。 - Tomas Petricek
可空类型在 F#-only API 中也不是“普通”的东西。因此,您可以双向调整它。 - Fyodor Soikin
显示剩余6条评论

3

你不应该使用异常。异常很烦人,尽量避免使用。只有在与期望你抛出异常或没有其他报告错误的方式的外部代码进行接口时才使用它们。

相反,如果你正在编写可能失败的函数,请使其返回一个“两者之一”的值 - 要么是结果,要么是错误。可以参考以下方式:

type Result<'t, 'e> = Ok of 't | Error of 'e

let f x =
   if x then Ok "ok"
   else Error "boo!"

这样,调用代码就不会“忘记”处理错误情况:编译器不会让它这样做。这是一件好事。
顺便说一下,F# 4.1在盒子里包含了Result类型(以及一些有用的实用程序)。查看该帖子中的示例。
另外值得关注的是:这里有一系列关于这个主题的文章和演示视频。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接