Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom print of object in C# Interactive

Consider this MCVE class:

public class Foo<T>
{
    private readonly T value;

    public Foo(T value)
    {
        this.value = value;
    }
}

When I evaluate and print such an object in C# Interactive, it looks like this:

> new Foo<string>("bar")
Foo<string> { }

That's not useful. I'd like it to look like this:

Foo<string> { "bar" }

How do I do that?

I've tried overriding ToString like this:

public override string ToString()
{
    return $"Foo{typeof(T)} {{ {value} }}";
}

This doesn't produce quite what I'd like:

> new Foo<string>("bar")
[Foo<System.String> { bar }]

There're at least three issues with this output:

  1. The value is not in quotes. I'd like it to be "bar" instead of bar.
  2. The type argument is displayed as System.String instead of string.
  3. The entire value is surrounded by square brackets. This is the least of my concerns.

Is there a way to make C# interactive display an object with a custom format?

I know that I can add a public property to the class in order to display the value, but I don't want to do that because of encapsulation concerns. To be clear, though, here's what I mean:

public class Foo<T>
{
    public Foo(T value)
    {
        Value = value;
    }

    public T Value { get; }
}

This prints closer to what I'd like:

> new Foo<string>("bar")
Foo<string> { Value="bar" }

but, as I wrote, I don't want to add a public property.

How do I get it to behave like the following?

> new Foo<string>("bar")
Foo<string> { "bar" }
> new Foo<int>(42)
Foo<int> { 42 }

Notice that when using anything else than a string (e.g. an int), there should be no quotes.

like image 831
Mark Seemann Avatar asked Apr 19 '18 09:04

Mark Seemann


2 Answers

You could use the [DebuggerDisplay] attribute to customize the object print. Beside overriding ToString(), you can use any methods/properties in this attribute. For example:

[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
public class Foo<T>
{
    private readonly T value;

    public Foo(T value)
    {
        this.value = value;
    }

    private string GetDebuggerDisplay()
    {
        var val = value is string ? "\"" + value + "\"" : value?.ToString();
        return $"Foo<{typeof(T).Name.ToLower()}> {{ {val} }}";
    }
}

This avoids having to override ToString(), or use a different implementation. Such as returning the string representation of T value.

You'll need to add a switch/case to transform class names such as Int32 to int.

The nq part of the [DebuggerDisplay] attribute removes the quotes around the value.

The result looks like:

> new Foo<string>("bar")
Foo<string> { "bar" }

For further reference, please check out the excellent blog post of Jared Parson about the [DebuggerDisplay] attribute: https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/.

like image 124
Henk Mollema Avatar answered Nov 04 '22 05:11

Henk Mollema


If you're OK with overriding the ToString() like in your first attempt, then you can do whatever you like there. This gets you closer to what you want and you can modify it as you see fit:

public override string ToString()
{
    string v = value is string ? $"\"{value}\"" : $"{value}";
    string t = typeof(T).Name.ToLower();
    return $"Foo<{t}> {{ {v} }}";
}
like image 2
Racil Hilan Avatar answered Nov 04 '22 03:11

Racil Hilan