Пользовательская печать объекта в C# Interactive

Рассмотрим этот класс MCVE:

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

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

Когда я оцениваю и распечатываю такой объект в C# Interactive, он выглядит так:

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

Это не полезно. Я хотел бы, чтобы это выглядело так:

Foo<string> { "bar" }

Как я могу это сделать?

Я пытался переопределить ToString как это:

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

Это не совсем то, что я хотел бы:

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

Есть как минимум три проблемы с этим выводом:

  1. Значение не в кавычках. Я бы хотел, чтобы это было "bar" вместо bar,
  2. Аргумент типа отображается как System.String вместо string,
  3. Вся стоимость заключена в квадратные скобки. Это наименьшее из моих опасений.

Есть ли способ заставить C# интерактивное отображение объекта в произвольном формате?

Я знаю, что могу добавить открытое свойство в класс для отображения значения, но я не хочу этого делать из-за проблем инкапсуляции. Чтобы быть ясным, вот что я имею в виду:

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

    public T Value { get; }
}

Это печатает ближе к тому, что я хотел бы:

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

но, как я уже писал, я не хочу добавлять публичную собственность.

Как мне заставить его вести себя следующим образом?

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

Обратите внимание, что при использовании чего-либо, кроме строки (например, int), не должно быть кавычек.

3 ответа

Решение

Как предполагает Кристиан Хелланг, это можно легко и просто решить, добавив [DebuggerDisplay] атрибут к классу:

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

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

Это решает все проблемы:

> new Foo<string>("bar")
Foo<string>("bar")
> new Foo<int>(42)
Foo<int>(42)
> new Foo<DateTime>(new DateTime(2018, 4, 23))
Foo<DateTime>([23.04.2018 00:00:00])

При рендеринге используются не фигурные скобки, а обычные скобки, указанные конструктором, но я считаю это только уместным.

Вы могли бы использовать [DebuggerDisplay] атрибут для настройки объекта печати. Рядом с переопределением ToString()Вы можете использовать любые методы / свойства в этом атрибуте. Например:

[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} }}";
    }
}

Это позволяет избежать необходимости переопределять ToString()или используйте другую реализацию. Например, возвращая строковое представление T value,

Вам нужно будет добавить переключатель / регистр для преобразования имен классов, таких как Int32 в int,

nq часть [DebuggerDisplay] Атрибут удаляет кавычки вокруг значения.

Результат выглядит так:

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

Для дальнейшего ознакомления, пожалуйста, проверьте отличный пост в блоге Джареда Парсона о [DebuggerDisplay] атрибут: https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/.

Если вы в порядке с переопределением ToString() как в вашей первой попытке, тогда вы можете делать все что угодно там. Это приближает вас к тому, что вы хотите, и вы можете изменить его так, как считаете нужным:

public override string ToString()
{
    string v = value is string ? $"\"{value}\"" : $"{value}";
    string t = typeof(T).Name.ToLower();
    return $"Foo<{t}> {{ {v} }}";
}
Другие вопросы по тегам