2

从数据库中检索唯一项目时,我需要满足客户端提供的 ID 没有数据的情况。例如,ID不正确或缓存数据已过期。

我在此示例中使用的特定 DB 客户端库是 ServiceStack.Redis,但我认为该原理适用于任何 CLR 库。

我已经使用如下所示的记录类型定义了我的数据结构。现在,当我使用客户端库检索数据库中不存在的键的数据时,将null返回一个值。这一点,我期待并且很高兴。问题是 F# 编译器不允许我在这种情况下进行模式匹配——即使它可能在运行时发生!

type MyRecordType = { id:int; name:string; desc:string }

let redis = new RedisClient("localhost")
let nullCheck =
    let item = redis.Get<MyRecordType> "xxx"
    // it is possible that item is null
    // but the compiler will not permit the match
    match item with
    | null -> None
    | _ -> Some item

Redis 客户端库包含一个“ContainsKey”方法,它返回一个布尔值,我可以先使用它,但这里不需要两次调用数据库。另一种方法是使用 C# 项目中定义的类作为数据结构,但这涉及太多开销。该属性也不允许在 上进行匹配null

我想知道我应该使用什么约定来处理这种情况,因为这似乎是一个非常常见的问题?

4

3 回答 3

4

不幸的是,您不能AllowNullLiteral在记录上使用。因此,最好的解决方案是只创建一个空值并像这样进行相等性检查

if item = Operators.Unchecked.defaultof<_> then None else Some(item)
于 2014-01-14T23:40:30.687 回答
4

我还发现了这篇使用拳击的文章。我认为它读起来稍微干净一些:

if (box item = null) then None else Some item
于 2014-01-15T00:08:41.633 回答
3

我认为这可能比我见过的其他选项好一点。F# 等于运算符非常强大,但当您真正想知道的只是您是否有一个空引用时,它也有点矫枉过正。

if obj.ReferenceEquals(item, null) then None else Some item

除非您进行大量比较,否则这可能无关紧要,但有一个真正的区别:

> for i = 1 to 100000000 do bar = null |> ignore;;
Real: 00:00:00.233, CPU: 00:00:00.234, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
> for i = 1 to 100000000 do obj.ReferenceEquals(bar, null) |> ignore;;
Real: 00:00:00.030, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
于 2014-01-16T17:46:46.597 回答