DebuggerDisplay 在运行时解析为字符串

问题描述 投票:0回答:4

有没有办法在运行时访问

DebuggerDisplayAttribute
显示的字符串?

对于我们的业务对象,我尝试获取有关异常处理的自动调试器信息。捕获异常时使用的实际对象应序列化为文本以增强异常消息。由于某些属性具有其他业务对象作为类型,因此如果递归使用,这可能会变得非常长。因此,我想序列化为类的

DebuggerDisplay
属性中已定义的信息。
ToString()
类的实现可能有所不同,并且不能用于此任务。

那么是否可以获取运行时调试器中显示的字符串?

c# attributes error-handling debugging
4个回答
5
投票

我不这么认为(至少在您没有付出一些努力的情况下)-我刚刚做了一些挖掘,发现这是一篇关于调试器显示最佳实践的文章。它没有直接关系,但它确实强调了一件事:

每个属性{表达式洞}必须 单独评估并这样做 对于该类型的每个实例一次 在每个调试器显示窗口中。

我希望一旦代码被中断,它就会使用调试器进行评估(有点类似于当你处于断点时使用立即窗口来评估语句)。

总而言之,对象的最终调试器显示值在运行时不可用,除非您愿意解析每个表达式漏洞并使用反射来自己评估它们。

本文建议提供调试器输出的最有效方法是使用私有方法对要显示的所有属性执行 String.Format。您可能需要考虑将其设为公共方法(可能在接口上)并使用它来检索异常信息。


2
投票

可能有某种方法可以提取该信息,但是使用如下属性重新定义这些类不是更容易吗:

[DebuggerDisplay("{InfoProperty}")]
class X {
    public string InfoProperty {
        get { return "Debug and display info here"; }
    }
}

然后将其包含在错误消息/日志中,而不是深入研究 Visual Studio 重建显示数据的方式。


当然,我假设您可以修改业务对象类,但情况可能并非如此......


0
投票
在运行时使用 Reflection 访问 DebuggerDisplayAttribute 并编写一些解析字符串的代码,然后再次使用 Reflection 来获取值。 不过,如果除了大括号内的属性和字段之外还有其他内容,这将不起作用。

无论如何,我强烈建议您听取 Mike 或 Paolo 的建议 - 如果您需要更改数百个类 - 然后找到一种自动更改它们的方法 - 可以使用 Resharper 的 Structural 之类的工具 搜索和替换,或正则表达式 - 应该不会花太长时间。

这个静态类提供了一种在运行时显示 DebuggerDisplayAttribute 显示的字符串的方法。它利用

0
投票
来评估表达式。它还使用一种扩展方法,如类下方所示。

InfoProperty

使用的一种自定义扩展方法如下:
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
                {
                    sb.Append( evaluator.Evaluate( expression ) );
                }
                catch (Exception ex)
                {
                    
                }

                cursorInOriginal = closeBrace + 1;
            }

            sb.Append( attribute.Value.Substring( cursorInOriginal ) );
            return sb.ToString( );
        }

        return string.Empty;
    }

    /// <summary>
    /// Display's 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 )}";
    }
}


© www.soinside.com 2019 - 2024. All rights reserved.