Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining DebuggerDisplay on complex types

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.

like image 487
Ignacio Soler Garcia Avatar asked Dec 30 '11 08:12

Ignacio Soler Garcia


3 Answers

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

like image 51
Toni Parviainen Avatar answered Oct 07 '22 01:10

Toni Parviainen


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.

like image 41
Chakrava Avatar answered Oct 06 '22 23:10

Chakrava


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

    }
like image 35
Mickey Perlstein Avatar answered Oct 07 '22 01:10

Mickey Perlstein