如何检查类型是否允许为空?

3

给定

let inline deserialize<'t> x :'t option = 
    printfn "Attempting to deserialize %A" typeof<'t>.Name
    try
        JsonConvert.DeserializeObject<'t>(x)
        |> Some
    with ex ->
        System.Diagnostics.Trace.WriteLine(sprintf "Error deserialization failed:%s" ex.Message)
        None

比如返回一个obj listnull。不允许FSharpList<_>为空。如果我不知道't是什么类型,我该如何询问F#即将返回的类型是否支持null,以便我可以相应地停止/抛出异常/处理?是否有反射标志或者Microsoft.FSharp.Reflection...方法可用?

2个回答

3
完整的答案涉及检查类型是否为记录(在这种情况下,永远不允许null),或者如果它是联合体(在这种情况下,如果类型具有CompilationRepresentationCustomAttribute,标志包含UseNullAsTrueValue成员,则允许使用null(有关更多详细信息,请参见https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/core.compilationrepresentationflags-enumeration-%5Bfsharp%5D)。
要回答第一个问题,您可以使用FSharpType模块中的IsRecord函数(请参见https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/reflection.fsharptype-class-%5Bfsharp%5D),而要回答第二个问题,则可以结合使用该同一模块上的IsUnion函数和CustomAttribute寻找。
如果类型是设置了UseNullAsTrueValue的联合体,则应该可以正常运行,只需将值发送出去即可。

这是否会跳过那些不应该为 null 的 F# 创建/定义的类呢? - Maslow
我不确定我理解了。一旦你创建一个类,它就可以为null。只有F#记录/联合才具有默认的非空期望。 - Chester Husk
如果在 F# 中定义的类没有 AllowNullLiteral 属性,那么如果您尝试设置 let x : TestFClass = null,它将无法编译。因此,所有依赖于该类型的 F# 程序集都期望它不为 null。因此,在 F# 中定义的类不应为 null。 - Maslow
这是一个很好的观点。我在Sharplab中创建了一个类,并发现编译器确实添加了一个属性来表示F#类:[<CompilationMapping(SourceConstructFlags.ObjectType)>]因此,您可以查找该属性的存在(没有 AllowNullLiteral属性)作为类型不允许为空的信号。这是比较的Sharplab链接:https://sharplab.io/#v2:DYLgZgzgPg9gDgUwHYAIDKBPCAXBBbAWAChjsNEUBhACgEoUBeYlFlPfAIwQCcVsALAJYQAdAFk6jFHWatiAbQA8AQWDAYAdwByAVzUAZQbm4BDYAD4AuqXIIUutTXoMUAY2AmIEFMgAmQA= - Chester Husk
太棒了,你能否更新你的答案,包括所有部分? - Maslow

1

我能想到的最好办法是将结果封装在一个盒子里(如果您正在反序列化结构体),然后使用null进行模式匹配:

  let inline deserialize<'t> x :'t option = 
      printfn "Attempting to deserialize %A" typeof<'t>.Name
      try
          let obj = Newtonsoft.Json.JsonConvert.DeserializeObject<'t>(x)
          match box obj with
          | null -> None
          | _ -> Some obj
      with ex ->
          System.Diagnostics.Trace.WriteLine(sprintf "Error deserialization failed:%s" ex.Message)
          None

  let r1 = deserialize<obj list> ("[1,2,3]") //val r1 : obj list option = Some [1L; 2L; 3L]
  let r2 = deserialize<obj list> ("null") //val r2 : obj list option = None

这只是检查 null,它并没有检查 't 是否允许为 null。 - Maslow
不确定为什么需要检查类型而不是结果值。但我猜简单的反射应该可以做到:let typeCanBeNull = (not typeof<'t>.IsValueType) || (System.Nullable.GetUnderlyingType(typeof<'t>) <> null); - 3615
1
因为在 F# 中允许值为空,所以如果它是空的话就没问题,否则我会检查该值不为空,并决定抛出/None/我选择的任何行为来处理这种情况。 - Maslow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接