鉴于以下内容:
[<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
).