在 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
,而後判斷此處的引用是否爲 null
:code
module Option let ofRecord r = match box r with | null -> None | _ -> Some r
由於 F# 中的 Record 類型底層就是使用引用類型來實現的,因此這裏並不會產生真正的裝箱操做,對性能的影響並不會太大。string