F# F中空引用的测试#

F# F中空引用的测试#,f#,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:

鉴于以下情况:

[<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}
    let name = if tweet.User <> null 
                  then tweet.User.Name
                  else tweet.Sender.Name
[]
类型TweetUser={
[]追随者:int
[]名称:字符串
[]Id:int
[]位置:字符串}
[]
类型Tweet={
[]Id:字符串
[]文本:字符串
[]网络化:布尔
[]DateStr:string
[]用户:TweetUser
[]发件人:TweetUser
[]来源:字符串}
使用
DataContractJsonSerializer(typeof)
反序列化将导致User或Sender字段为null(至少调试器是这么告诉我的)

如果我尝试写以下内容:

[<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}
    let name = if tweet.User <> null 
                  then tweet.User.Name
                  else tweet.Sender.Name
let name=if tweet.User null
然后是tweet.User.Name
else tweet.Sender.Name
编译器发出错误:“类型'TweetUser'没有'null'作为正确的值”


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

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

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

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

let inline isNull x = x = Unchecked.defaultof<_>
让内联isNull x=x=Unchecked.defaultof

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

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

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

未选中。defaultof
正在做正确的事情,并为您的记录类型生成空值;问题在于默认的相等运算符使用通用结构比较,这要求您在使用F类型时始终遵循F的规则。在任何情况下,空检查实际上只在一开始就保证引用比较。

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

isNull <| box obj

如果tweet.User未选中.defaultof
有效吗?如果不是,则始终存在.Unchecked.defaultof编译,但在运行时不起作用(不正确匹配null)。AllowNullLiteral对于记录字段无效。但是,在记录字段上不允许有好的建议.AllowNullLiteral。与Unchecked.defaultof比较时访问tweet.User的程序错误。换句话说,只要引用tweet.User在其为null时进行比较就足以导致错误。奇怪。@MikeWard该属性需要应用于该类型-在您的例子中是
TweetUser
-然后它指定该类型的任何出现(不仅在字段中,而且在
if
表达式中)都可以有
null
值。我也试过了。AllowNullLiteral无法应用于记录类型。reference.equals事物按预期工作。只是其中一个我们不得不忍受的F#interop怪胎。整体而言,非常喜欢langauge。建议的
isNull
函数似乎不起作用。。。如果调用
Unchecked.defaultof |>isNull
,则结果是NullReferenceException。一旦理解了该概念,就有意义了。谢谢在此处使用
未选中的.defaultof
是否比仅使用
null
有优势?后者似乎可以避免函数调用,并允许它为结构返回正确的结果。@DaxFohl:F#type的实例在默认情况下不允许实例化为
null
(请参阅)。这里使用的约束特别禁止值类型,因为它们在语义上没有意义。最后,
Unchecked。defaultof
是编译器的固有属性(就像C#中的
default
),因此那里没有函数调用。@DaxFohl:后来才发现,我对
Unchecked.defaultof
的调用是误导性的,因为它总是转换为
Unchecked。defaultof
,所以
null
在这里毕竟是合适的。
match box obj with
| null -> (* obj is null *)
| _ -> (* obj is not null *)