在复杂类型上链接DebuggerDisplay

16

我有几个定义了DebuggerDisplay属性的类。我想知道是否有一种方法可以基于另一个DebuggerDisplay属性来定义一个属性。如果我有以下类:

[DebuggerDisplay ("Text = {Text}")]
class A
{
    public string Text {get;set;}
}

[DebuggerDisplay ("Property = {Property}")]
class B
{
    public A Property {get; set;}
}

我希望在B类的实例上看到A类的定义,就像在A类DebuggerDisplay属性中定义的那样。但是当查看B类对象时,我得到的是A类ToString()方法的调试器。


2
[DebuggerDisplay] 只适用于简单的情况。显然你需要使用 [DebuggerTypeProxy]。 - Hans Passant
相关链接:https://dev59.com/ImQm5IYBdhLWcg3w2R5J - Chris Marisic
3个回答

6

我不确定是否正确理解了您的问题,但尝试以下方法:

[DebuggerDisplay("Property = {Property.Text}")]
public class B
{
    public A Property { get; set; }
}

这将显示A的文本属性。

如果您需要更复杂的控制,可以使用DebuggerTypeProxyAttribute


2
如果(这是情况)类A有几个属性。我认为必须将A DebuggerDisplay属性重写到B类中似乎不是一个好的选择。 - Ignacio Soler Garcia

2

来自https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/(我添加了条件编译指令)

这篇文章讲述了在使用 C# 调试器时,如何通过 DebuggerDisplay 属性来定制显示格式。该属性可以用于自定义对象显示的文本内容,以便更方便地进行调试。在给出的代码示例中,作者提供了最佳实践,并且演示了如何将此功能与条件编译指令一起使用。
#if DEBUG
    [DebuggerDisplay("{DebuggerDisplay}")]
    public sealed class Student {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    private string DebuggerDisplay {
        get { return string.Format("Student: {0} {1}", FirstName, LastName);}
    }
}
#endif

这类似于Mickey Perlstein的答案(清除格式化调试器字符串的clear属性),而不需要重写ToString()(毕竟可能需要其他目的)。
源还提供了许多有关DebuggerDisplay的其他好提示,包括一些性能考虑。
编辑
由于这只是调试代码,违反OOP(从外部访问私有属性)并不那么糟糕...但我们在这里相当严重地违反了它。
private string DebuggerString {
    get {
        StringBuilder sb = new StringBuilder();
        sb.Append("Whatever you want your Parent class' Debugger Text To Say");

        var properties = typeof(GroupQuote).GetProperties()
            //get the properties with the DebuggerDisplay attribute and our property
            .Where(x =  > x.PropertyType.IsDefined(typeof(DebuggerDisplayAttribute))
                     && x.PropertyType.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Any(y =  > y.Name == "DebuggerString"));

        foreach(PropertyInfo property in properties) {
            object itemWithProperty = property.GetValue(this);
        //we have to check our property for null, otherwise trying to get its DebuggerString property will throw an exception
        if (itemWithProperty != null) {
                PropertyInfo privateDebuggerProperty = property.PropertyType.GetProperty("DebuggerString", BindingFlags.NonPublic | BindingFlags.Instance);
                sb.Append(privateDebuggerProperty.GetValue(itemWithProperty)as string);
            }
        }
        return sb.ToString();
    }
}

在我编写和测试的代码中,我的父类的一些属性显示了DebuggerDisplay被定义,但实际上并没有(可能是继承的问题?)。 我增加了一个额外的检查,以便我们只查找实际具有DebuggerString的属性。

示例


2
这不是一个坏主意,但主要问题——也是我创建这篇帖子的原因——仍然没有得到解答。如果属性是带有已定义DebuggerDisplay的对象,则无法使用已定义的属性来表示它们(除非我将DebuggerDisplay属性公开)。 - Ignacio Soler Garcia
你可以使用反射(以及对面向对象编程原则的健康不尊重)来检索私有属性。我已经编辑了我的答案,加入了一个基本示例。 - Chakrava
嘿。好观点,但我认为这有点过度设计,只是为了显示一些调试信息。关键是:调试器知道类型具有DebuggerDisplay属性,因此如果它找到一个指向已经定义了该属性的对象的DebuggerDiplay,请使用它! - Ignacio Soler Garcia
1
所以我不完全知道DebuggerDisplayAttribute是如何工作的,但是看它的反编译代码,它似乎只是存储你给它的字符串,例如:“{aProperty}”,我认为调试器本身会解释该字符串以检索值,在属性本身没有花哨的代码。因此,虽然调试器确实检查父对象是否具有该属性(以显示它而不是ToString()),但它不会知道任何父对象的属性是否也具有该属性,除非以我那样的方式进行查找。 - Chakrava

1

我知道这不是“正确的编码方式”,但由于我无法链接我的实体,所以我决定回到旧的方法。只需覆盖ToString()方法,然后链接就变得轻而易举。

    public partial class Tld
{
    public override string ToString()
    {
        return this.Name;
    }    
}

public partial class Domain
{
    public override string ToString()
    {
        return this.DomainName + "." +this.Tld.ToString();
    } 

    public  Domain (string domain, string tld):this( domain, new Tld(tld))
    {

    }
    public Domain(string domain, Tld tld):this()
    {
        this.DomainName = domain;
        this.Tld = tld;

    }
}


public partial class Url
{
    public override string ToString()
    {
        return this.Scheme + "://" + this.Subdomain + this.Domain.ToString() + ((string.IsNullOrWhiteSpace(this.Path)) ? "" :  this.Path);
    }
    public Url (string scheme, string subdomain, string domain, string tld, string path):this(new Tld(tld),domain, subdomain,scheme,path){}

    public Url(Tld tld, string domainName, string subdomain, string scheme, string path): this(new Domain(domainName, tld),subdomain,scheme,path){}

     public Url(Domain domain, string subdomain, string scheme, string path):this()
    {
        this.Domain = domain;
        this.Path = path;
        this.Scheme = scheme;
        this.Subdomain = subdomain;

    }

}


public void Domain_Create_GOOD()
    {
     Domain expected = new Domain("google","co.nz");

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接