在F#中测试空引用

Mik*_*ard 13 f#

鉴于以下内容:

[<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}
Run Code Online (Sandbox Code Playgroud)

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

如果我尝试写下面的内容:

    let name = if tweet.User <> null 
                  then tweet.User.Name
                  else tweet.Sender.Name
Run Code Online (Sandbox Code Playgroud)

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

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

ild*_*arn 18

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

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

要么

let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)
Run Code Online (Sandbox Code Playgroud)

Unchecked.defaultof<_>正在做正确的事情并为您的记录类型生成空值; 问题是默认的相等运算符使用通用结构比较,它希望您在使用F#类型时始终按F#规则进行播放.在任何情况下,无效检查实际上只能保证参考比较.


Tom*_*cek 14

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

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

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

let inline isNull x = x = Unchecked.defaultof<_>
Run Code Online (Sandbox Code Playgroud)

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

  • 提出的`isNull`函数似乎不起作用...如果你调用`Unchecked.defaultof <Foo> |> isNull`,结果是NullReferenceException. (3认同)