Last active
January 31, 2019 04:39
-
-
Save ZeekoZhu/34a006c1ad002de59418c08abc92d187 to your computer and use it in GitHub Desktop.
[如何对付运行时可能为 null 的 Record Type] 尽管 F# 不允许使用 Record 类型表达 null 的含义,但在运行时 Record 仍然可能为 null,如何处理这样的问题? #FSharp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
在 F# 中,Record Type 是无法表达 `null` 语义的,例如,一个 Record 变量不能够使用 `null` 字面量赋值,接收 nullable(这里并不是指 BCL 中的 `Nullable<T>` 类型,而是指 C# 8.0 之前的引用类型)作为参数的函数不能使用 Record 作为参数: | |
```fsharp | |
type Foo = {Id: string} | |
let foo: Foo = null // 编译错误 | |
let foo = {Id: "2333"} // 编译通过 | |
let fooOp = Option.ofObj foo // 编译错误 | |
``` | |
F# 的设计者可能认为 Record 作为一个典型的函数式语言特性,使用 option 来表达 nullable 会更加 Functional,所以就禁止了 Record 与 `null` 的直接转换。这种愿景非常美好,但是实际上大部分的 .Net 生态环境都是使用 C# 来构建的,比如 Linq。下面的一段代码就会在 F# 中引发可怕的“空引用异常”: | |
```fsharp | |
open System.Linq | |
let foos: Foo list = [] | |
let nill = foos.FirstOrDefault() | |
//> let nill: Foo | |
prinrf "%A" nill.Id //<-- 空引用异常 | |
``` | |
可以看到,`FirstOrDefault` 的返回值尽管是 `Foo` 类型,但是在运行时其结果永远都是 `null`,又因为 F# 禁止了 Record 与 `null` 相关的比较操作,所以此处无法直接进行判断结果是否为 `null`。 | |
我采用的做法就是进行类型转换,首先将 Record 类型转换成 `obj`,然后判断此处的引用是否为 `null`: | |
```fsharp | |
module Option | |
let ofRecord r = | |
match box r with | |
| null -> None | |
| _ -> Some r | |
``` | |
因为 F# 中的 Record 类型底层就是使用引用类型来实现的,所以这里并不会产生真正的装箱操作,对性能的影响并不会太大。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment