打印受歧视联合的案例标识符

Print case-identifier of discriminated union

我有一个 Option 类型:

type Option<'a> =
    | Some of 'a
    | None

    override x.ToString() = sprintf "%A" x

printf "%A" None // "None"
printf "%A" (Some 1) // "Some 1"

据推测,在一个函数中我想打印 Some 1,但在另一个函数中我想打印它的大小写标识符,即 Some(丢弃“1”值)。我该怎么做?

您可以使用匹配项:

override x.ToString() = 
    match x with
    | Some _ -> "Some"
    | None -> "None"

处理您的评论: 在这种情况下,我不会重写 ToString 方法,而是根据预期行为进行单独匹配。或者只是定义一个辅助函数来打印选项而不指定内容;

let printEmpty myOpt = 
    match myOpt  with
    | Some _ -> "Some"
    | None -> "None"

这样您可以使用 sprintf "%A" myOpt 打印有内容,或 printEmpty myOpt 打印无内容。

如果您想要一种通用的方式来实现它而不是为每个类型实现一个成员,您可以为此使用反射:

open Microsoft.FSharp.Reflection

let caseLabel<'t> (x: 't) = 
    let typ = typeof<'t>
    if FSharpType.IsUnion(typ) 
        then
            let case, _ = FSharpValue.GetUnionFields(x, typ)
            Some case.Name
        else
            None

为了它的价值,我将@scrwtp 的版本包括在 "type extension" 形式中,而不是 Option<'a> 的原始重新实现。理由很简单,最近定义的类型混淆了在 ToString 部分(原始问题中的类型 Option<'a> 定义)中 caseLabel 的使用。

注意。因为我们不能从 DU 继承(它没有构造函数),所以我们不能重载 ToString。所以带有 ToString 的扩展仍然默认为原始 ToString。因此,静态模块样式可能更可取,因为我们可以显式访问新行为。否则, sprintf / printf 的行为将访问原始的 ToString,这不是我们根据问题想要的。我认为这是一个编译器错误。错误提交 here

顺便说一句:FSI 漂亮的打印可以缓解仅 FSI 场景(TBD)的这种情况。

open Microsoft.FSharp.Reflection

let caseLabel (x:'x) = 
    typeof<'x> |> fun typ ->
      if FSharpType.IsUnion(typ) 
          then FSharpValue.GetUnionFields(x, typ) ||> fun case _ -> Some(case.Name)
          else None

type Option<'t> with 
  static member toString x = 
    match caseLabel x with
    | Some(label) -> label
    | None        -> "None"  

sprintf "%s" <| (Some 1 |> Option.toString)  // returns "Some"
sprintf "%s" <| (None |> Option.toString)    // returns "None"