16

I have several classes defining the DebuggerDisplay attribute. I want to know if there is a way to define one DebuggerDisplay attribute based on another one. If I have the following classes:

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

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

I would like to see on instances of B the A class as it is defined on the class A DebuggerDisplay attribute. Instead of that I'm getting the class A ToString() method onto the debugger while viewing class B objects.

Ignacio Soler Garcia
  • 21,122
  • 31
  • 128
  • 207

3 Answers3

6

Not sure if I understood your problem correctly but try:

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

This will Display the A's Text property.

If you need more complex control you can use DebuggerTypeProxyAttribute

Toni Parviainen
  • 2,217
  • 1
  • 16
  • 15
  • 2
    What if (and this is the case) class A has several properties. Having to rewrite the A DebuggerDisplay attribute into class B looks as a bad option to me. – Ignacio Soler Garcia Dec 30 '11 at 10:44
2

From https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/ (I added the conditional compilation directives)

#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

This is similar to Mickey Perlstein's answer (clear Property that formats the debugger's string) without needing to override ToString() (which might be needed for another purpose after all.)

The source also has a number of other good tips for DebuggerDisplay, including some performance considerations.

Edit


Since this is debugging code anyways it's not as bad to violate OOP (accessing private property from outside)...but we're violating it pretty hard here.

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();
    }
}

Example

In the code I wrote and tested this in I was having some properties of my Parent class showing that DebuggerDisplay was defined when it wasn't (possibly an inheritance thing?). I added an additional check so that we only look for DebuggerString on properties that actually have it.

Community
  • 1
  • 1
Chakrava
  • 823
  • 7
  • 10
  • 2
    It is not a bad idea but the main issue, the one that made me create this post is still unanswered. If the properties were objects with an already defined DebuggerDisplay I couldn't represent them with the already defined attribute (unless I make the DebuggerDisplay property public). – Ignacio Soler Garcia Mar 02 '16 at 20:46
  • You can use reflection (and a healthy lack of respect for OOP principles) to retrieve private properties. I've edited my answer with a basic example. – Chakrava Mar 03 '16 at 02:15
  • Heh. Good point but I actually think this is a bit overkill just to show some debug information. The point is: the debugger knows that a type has a DebuggerDisplay attribute so if it finds a DebuggerDiplay pointing to an object that already has the attrribute defined use it! – Ignacio Soler Garcia Mar 03 '16 at 14:17
  • 1
    So I'm not 100% on how the DebuggerDisplayAttribute works, but looking at it's decompiled code it looks like it just stores the string you give it, i.e.: "{aProperty}", I think the debugger itself interprets that string to retrieve the value, there's no fancy code in the attribute itself. So while the debugger does check if the parent object has the attribute (to display it instead of ToString()), it wouldn't know if any of the parent object's properties also have the attribute without looking in much the way that my gnarly reflection does. – Chakrava Mar 03 '16 at 20:06
1

I know this isn't "correct coding" but since I can't chain my entites, I have decided to get back to the old ways. Just override the ToString() method. then chaining is a piece of cake.

    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");

    }
Mickey Perlstein
  • 2,508
  • 2
  • 30
  • 37