出于某种原因(我还没有调查原因)not (obj.ReferenceEquals(value, null)) 的性能比value <> null 好得多。我写了很多从 C# 中使用的 F# 代码,所以我保留了一个 "interop" module 以方便处理 null。此外,如果您希望在模式匹配时首先使用“正常”情况,您可以使用活动模式:
let (|NotNull|_|) value =
if obj.ReferenceEquals(value, null) then None
else Some()
match value with
| NotNull ->
//do something with value
| _ -> nullArg "value"
如果您想要一个简单的 if 语句,这也可以:
let inline notNull value = not (obj.ReferenceEquals(value, null))
if notNull value then
//do something with value
更新
以下是有关性能差异的一些基准和其他信息:
let inline isNull value = (value = null)
let inline isNullFast value = obj.ReferenceEquals(value, null)
let items = List.init 10000000 (fun _ -> null:obj)
let test f = items |> Seq.forall f |> printfn "%b"
#time "on"
test isNull //Real: 00:00:01.512, CPU: 00:00:01.513, GC gen0: 0, gen1: 0, gen2: 0
test isNullFast //Real: 00:00:00.195, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
775% 的加速——还不错。查看 .NET Reflector 中的代码后:ReferenceEquals 是本机/非托管函数。 = 操作符调用HashCompare.GenericEqualityIntrinsic<'T>,最终到达内部函数GenericEqualityObj。在 Reflector 中,这种美反编译为 122 行 C#。显然,平等是一个复杂的问题。对于null-检查一个简单的引用比较就足够了,因此您可以避免更微妙的相等语义的成本。
更新 2
模式匹配还避免了相等运算符的开销。以下函数的执行方式与 ReferenceEquals 类似,但仅适用于在 F# 之外定义或使用 [<AllowNullLiteral>] 修饰的类型。
let inline isNullMatch value = match value with null -> true | _ -> false
test isNullMatch //Real: 00:00:00.205, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
更新 3
正如 Maslow 的评论中所指出的,isNull operator 是在 F# 4.0 中添加的。它的定义与上面的isNullMatch 相同,因此性能最佳。