如何對付運行時可能爲 null 的 Record Type

在 F# 中,Record Type 是沒法表達 null 語義的,例如,一個 Record 變量不可以使用 null 字面量賦值,接收 nullable(這裏並非指 BCL 中的 Nullable<T> 類型,而是指 C# 8.0 以前的引用類型)做爲參數的函數不能使用 Record 做爲參數:函數

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# 中引起可怕的「空引用異常」:性能

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,而後判斷此處的引用是否爲 nullcode

module Option

let ofRecord r =
    match box r with
    | null -> None
    | _ -> Some r

由於 F# 中的 Record 類型底層就是使用引用類型來實現的,因此這裏並不會產生真正的裝箱操做,對性能的影響並不會太大。string

相關文章
相關標籤/搜索