15

鉴于以下情况:

[<DataContract>]
type TweetUser = {
    [<field:DataMember(Name="followers_count")>] Followers:int
    [<field:DataMember(Name="screen_name")>] Name:string
    [<field:DataMember(Name="id_str")>] Id:int
    [<field:DataMember(Name="location")>] Location:string}

[<DataContract>]
type Tweet = {
    [<field:DataMember(Name="id_str")>] Id:string
    [<field:DataMember(Name="text")>] Text:string
    [<field:DataMember(Name="retweeted")>] IsRetweeted:bool
    [<field:DataMember(Name="created_at")>] DateStr:string
    [<field:DataMember(Name="user", IsRequired=false)>] User:TweetUser
    [<field:DataMember(Name="sender", IsRequired=false)>] Sender:TweetUser
    [<field:DataMember(Name="source")>] Source:string}

反序列化DataContractJsonSerializer(typeof<Tweet[]>)将导致 User 或 Sender 字段为空(至少这是调试器告诉我的)。

如果我尝试编写以下内容:

    let name = if tweet.User <> null 
                  then tweet.User.Name
                  else tweet.Sender.Name

编译器发出错误:“类型'TweetUser'没有'null'作为正确值”

在这种情况下如何测试空值?

4

3 回答 3

19

循环扩展@Tomas 的答案;-]

let name = if not <| obj.ReferenceEquals (tweet.User, null)
              then tweet.User.Name
              else tweet.Sender.Name

或者

let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)

Unchecked.defaultof<_>正在做正确的事情并为您的记录类型生成空值;问题是默认的相等运算符使用通用结构比较,它希望您在使用 F# 类型时始终遵守 F# 的规则。在任何情况下,空检查实际上只需要首先进行引用比较。

于 2012-05-25T00:00:31.087 回答
15

要向@ildjarn 的注释添加一些详细信息,您会收到错误消息,因为 F# 不允许null用作在 F# 中声明的类型的值。这样做的动机是 F# 试图从纯 F# 程序中消除null值(和)。NullReferenceException

但是,如果您使用的是未在 F# 中定义的类型,您仍然可以使用null(例如,当调用System.Random作为参数的函数时,您可以给它null)。这是互操作性所必需的,因为您可能需要传递null给 .NET 库或作为结果接受它。

在您的示例中,TweetUser是在 F# 中声明的(记录)类型,因此该语言不允许将null其视为 type 的值TweetUser。但是,您仍然可以null通过即反射或从 C# 代码获取值,因此 F# 提供了一个“不安全”函数,该函数创建null任何类型的值 - 包括 F# 记录,通常不应具有null值。这是Unchecked.defaultOf<_>函数,您可以使用它来实现这样的助手:

let inline isNull x = x = Unchecked.defaultof<_>

或者,如果您使用AllowNullLiteral属性标记类型,那么您就是在告诉 F# 编译器它应该允许null作为该特定类型的值,即使它是在 F# 中声明的类型(并且通常不允许null)。

于 2012-05-24T23:42:08.740 回答
3

虽然这个问题很老,但我没有看到任何拳击的例子来解决这个问题。在我的演示者不允许空文字但可以从视图中设置的情况下,我更喜欢使用装箱。

isNull <| box obj

或者

let isMyObjNull = isNull <| box obj

或者

match box obj with
| null -> (* obj is null *)
| _ -> (* obj is not null *)
于 2017-02-13T16:48:23.937 回答