RMills330
RMills330

Reputation: 329

Enhanced debug information for discriminated unions

From what I can gather, the DebuggerDisplayAttribute cannot be applied to individual levels of a discriminated union, only to the top-level class.

The corresponding documentation suggests that overriding the ToString() method is an alternative way.

Taking the following example:

type Target =
    | Output of int
    | Bot of int

    override this.ToString () =
        match this with
        | Output idx -> $"output #{idx}"
        | Bot idx -> $"bot #{idx}"

[<EntryPoint>]
let main _ =    
    let test = Bot 15
    
    0

When breaking on the return from main and placing a watch on test, the VS2019 debugger is showing Bot 15 rather than bot #15.

The documentation also suggests that:

Whether the debugger evaluates this implicit ToString() call depends on a user setting in the Tools / Options / Debugging dialog box.

I cannot figure out what user setting it is referring to.

Is this not available in VS2019 or am I just missing the point?

Upvotes: 3

Views: 163

Answers (1)

Brian Berns
Brian Berns

Reputation: 17038

The main problem here is that the F# compiler silently emits a DebuggerDisplay attribute to override the default behavior described in the documentation you're looking at. So overriding ToString alone is not going to change what the debugger displays when debugging an F# program.

F# uses this attribute to implement its own plain text formatting. You can control this format by using StructuredFormatDisplay to call ToString instead:

[<StructuredFormatDisplay("{DisplayString}")>]
type Target =
    | Output of int
    | Bot of int

    override this.ToString () =
        match this with
        | Output idx -> $"output #{idx}"
        | Bot idx -> $"bot #{idx}"

    member this.DisplayString = this.ToString()

If you do this, the Visual Studio debugger will display "bot #15", as you desire.

Another option is to explicitly use DebuggerDisplay yourself at the top level, as you mentioned:

[<System.Diagnostics.DebuggerDisplay("{ToString()}")>]
type Target =
    | Output of int
    | Bot of int

    override this.ToString () =
        match this with
        | Output idx -> $"output #{idx}"
        | Bot idx -> $"bot #{idx}"

FWIW, I think the direct answer to your question about the Tools / Options / Debugging setting is "Show raw structure of objects in variables windows". However, this setting isn't really relevant to the problem you're trying to solve.

Upvotes: 5

Related Questions