鉴于以下内容:
[<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'作为正确的值”
在这种情况下如何测试空值?
答案 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 *)