This static class provides a method to display the string shown by DebuggerDisplayAttribute at runtime. It utilizes https://github.com/codingseb/ExpressionEvaluator to evaluate the expressions. It also uses two extension methods, shown below the class.
using System;
using System.Diagnostics;
using System.Text;
using CodingSeb.ExpressionEvaluator;
namespace CentralFunctionality.UtilityTypes;
public static class DisplayDebugView
{
/// <summary>
/// Reproduces the output specified in <see cref="DebuggerDisplayAttribute"/>. Strings are displayed without quotes, regardless of the use of ",nq", consistent with their display in Rider but not Visual Studio.
/// </summary>
/// <param name="obj">The object to get the debugger display for.</param>
/// <returns>The string produced by the DebuggerDisplay value.</returns>
public static string GetExpression( object obj )
{
if( obj.GetType( ).GetCustomAttributes( typeof(DebuggerDisplayAttribute), true ).HasOne( out var untypedAttribute ) )
{
DebuggerDisplayAttribute attribute = untypedAttribute as DebuggerDisplayAttribute;
if( String.IsNullOrWhiteSpace( attribute.Value ) )
return attribute.Value;
var evaluator = new ExpressionEvaluator( obj );
StringBuilder sb = new StringBuilder( );
int cursorInOriginal = 0;
while( attribute.Value.IndexOf( '{', cursorInOriginal ) >= 0 )
{
int openBrace = attribute.Value.IndexOf( '{', cursorInOriginal );
sb.Append( attribute.Value.Substring( cursorInOriginal, openBrace - cursorInOriginal ) );
int closeBrace = attribute.Value.IndexOf( '}', openBrace );
string expression = attribute.Value.Substring( openBrace + 1, closeBrace - (openBrace + 1) );
if( expression.EndsWith( ",nq" ) ) expression = expression.Substring( 0, expression.Length - 3 );
try
{
object expr = evaluator.Evaluate( expression );
if( expr is not string exprString )
exprString = GetExpression( expr );
sb.Append( exprString );
}
catch (Exception ex)
{
}
cursorInOriginal = closeBrace + 1;
}
sb.Append( attribute.Value.Substring( cursorInOriginal ) );
return sb.ToString( );
}
return obj.ToString();
}
/// <summary>
/// Displays the type name and debugger display expression in a manner similar to the display in Rider's debugger.
/// </summary>
/// <param name="obj">The object for which to display a line of information.</param>
/// <returns>The object description.</returns>
public static string GetAll( object obj )
{
return $"{{{obj.GetType().Namespace}.{obj.GetType().PrettyName()}}} {GetExpression( obj )}";
}
}
The custom extension methods used are as follows:
public static bool HasOne<TSource>( this IEnumerable<TSource> source, out TSource value, Func<TSource,bool> predicate = null )
{
try
{
value = predicate != null ? source.Single( predicate )
: source.Single( );
return true;
}
catch ( InvalidOperationException ex )
{
value = default(TSource);
return false;
}
}
public static string PrettyName( this Type type )
{
// From https://mcmap.net/q/372676/-c-quot-pretty-quot-type-name-function
// That source had other, more complex options that might handle more complex type names.
if ( type.GetGenericArguments( ).Length == 0 )
{
return type.Name;
}
try
{
var genericArguments = type.GetGenericArguments( );
var typeDefeninition = type.Name;
var unmangledName = typeDefeninition.Substring( 0, typeDefeninition.IndexOf( "`" ) );
return unmangledName + "<" + String.Join( ",", genericArguments.Select( PrettyName ) ) + ">";
}
catch
{
return type.Name;
}
}
I would suspect there are things that could be done in DebuggerDisplayAttribute that might work in Visual Studio and/or Rider that could break this, but this should support the functionality currently documented at https://learn.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute?view=vs-2022, in the same manner that Rider currently supports it.