在F#中测试空引用

时间:2012-05-24 23:19:46

标签: 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}

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

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

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

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

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

3 个答案:

答案 0 :(得分:17)

循环扩展@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#规则进行播放。在任何情况下,无效检查实际上只能保证参考比较。

答案 1 :(得分:13)

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

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

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

AllowNullLiteral

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

答案 2 :(得分:2)

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

isNull <| box obj

let isMyObjNull = isNull <| box obj

match box obj with
| isNull -> (* obj is null *)
| _ -> (* obj is not null *)