可以访问属性的子“DebuggerDisplay”属性吗?

时间:2013-06-25 12:32:13

标签: c# .net visual-studio debugging debuggerdisplay

当前状态

有两个班级:

[DebuggerDisplay(@"One = {One}, two = {Two}")]
public class A
{
    public int One { get; set; }
    public B Two { get; set; }
}

[DebuggerDisplay(@"Three = {Three}")]
public class B
{
    public int Three { get; set; }
}

使用它们:

var a = new A {One = 5, Two = new B {Three = 10}};

在调试器内部,a显示的工具提示值为

  

一个= 5,两个= {DebuggerDisplayTest.B}

目标

我想要的是像

  

一个= 5,两个='三个= 10'

我知道这可以通过覆盖类ToString()的{​​{1}}方法来实现。这只是感觉不对,因为我在我的应用程序中编写代码仅用于调试。

我也知道使用类似于

的字符串
B

也会奏效。这对我来说也不合适,因为它要求班[DebuggerDisplay(@"One = {One}, two = 'Three = {Two.Three}'")] 了解班级A

我希望有更多方法可以将类型B DebuggerDisplay的值“注入”类B中该类型的实例。

问题

以某种方式可以访问“has-a”合成类的A属性中成员的DebuggerDisplay属性吗?

更新

根据this SO answer,我的要求可能是不可能的。也许一个好的解决方案是在类DebuggerDisplay中覆盖ToString并执行一些B并使用Debugger.IsAttached property仅在调试器内部表现不同。

类似的东西:

if..else

3 个答案:

答案 0 :(得分:4)

从OP

复制可能的解决方案

根据SO answer,我的要求可能是不可能的。也许一个好的解决方案是覆盖B类中的ToString并执行一些if..else并使用Debugger.IsAttached property仅在调试器内部表现不同。

类似的东西:

[DebuggerDisplay(@"Three = {Three}")]
public class B
{
    public int Three { get; set; }

    public override string ToString()
    {
        if (Debugger.IsAttached)
        {
            return string.Format(@"Three = {0}", Three);
        }
        else
        {
            return base.ToString();
        }
    }
}

答案 1 :(得分:4)

[免责声明我隶属于OzCode]

您可以使用支持嵌套调试信息的OzCode Reveal featureReveal in action!
优点是您不需要更改生产代码,一旦为实例定义它,它将自动用于该类型的所有实例。

答案 2 :(得分:1)

拼凑了一些我已经提出这个解决方案的东西。它有一个警告,它希望你遵循JSFiddle。使用C#6(https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class B
{
    public int Three { get; set; }

    private string DebuggerDisplay => $"Three = {Three}";
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class A
{
    public int One { get; set; }
    public B Two { get; set; }

    private string DebuggerDisplay => $"One = {One}, two = {Two.ReadDebuggerDisplay()}";
}

你需要确保你有适当的导入,因为你需要将这个帮助器放在与需要读取子调试器显示的代码相关的位置。

public static class ReflectionHelper
{
    // https://stackoverflow.com/a/13650728/37055
    public static object ReadProperty(
        this object target,
        string propertyName)
    {
        var args = new[] {CSharpArgumentInfo.Create(0, null)};
        var binder = Binder.GetMember(0, propertyName, target.GetType(), args);
        var site = CallSite<Func<CallSite, object, object>>.Create(binder);
        return site.Target(site, target);
    }

    public static string ReadDebuggerDisplay(
        this object target, 
        string propertyName = "DebuggerDisplay")
    {
        string debuggerDisplay = null;
        try
        {
            var value = ReadProperty(target, propertyName) ?? "<null object>";

            debuggerDisplay = value as string ?? value.ToString();
        }
        catch (Exception)
        {
            // ignored
        }
        return debuggerDisplay ?? 
              $"<ReadDebuggerDisplay failed on {target.GetType()}[{propertyName}]>";
    }
}

我认为这是一种非常公平的纯洁和实用的平衡,可以降低实现这一目标的摩擦力。如果您不太关心纯度,可以将DebuggerDisplay公开化。我更喜欢ReadDebuggerDisplay以“无类型”方式运行(避免公开访问DebuggerDisplay所需的通用约束和接口)。