Пользовательская печать объекта в 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 }]
Есть как минимум три проблемы с этим выводом:
- Значение не в кавычках. Я бы хотел, чтобы это было
"bar"
вместоbar
, - Аргумент типа отображается как
System.String
вместоstring
, - Вся стоимость заключена в квадратные скобки. Это наименьшее из моих опасений.
Есть ли способ заставить 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} }}";
}