Export (0) Print
Expand All

Using DebuggerDisplay Attribute

The DebuggerDisplay attribute (System.Diagnostics.DebuggerDisplayAttribute) controls how a class or field is displayed in the debugger variable windows. This attribute can be applied to:

  • Classes

  • Structs

  • Delegates

  • Enums

  • Fields

  • Properties

  • Assemblies

The DebuggerDisplay attribute has a single argument, which is a string to be displayed in the value column for instances of the type. This string can contain braces ({ and }). Text within a pair of braces will be evaluated as a field, property or method.

If a C# object has an overridden ToString(), the debugger will call the override and show its result instead of the standard {<typeName>}. Thus, if you have overridden ToString(), you do not have to use DebuggerDisplay. If you use both, the DebuggerDisplay attribute takes precedence over the ToString() override.

Whether the debugger evaluates this implicit ToString() call depends on a user setting in the Options dialog box (Debugging category, General page). Visual Basic does not implement this implicit ToString() evaluation.

Important note Important

If the Show raw structure of objects in variables windows check box is selected in the Tools Options dialog box, then the DebuggerDisplay attribute is ignored.

The following table shows some possible uses of the DebuggerDisplay attribute and example outputs.

Attribute

Output appearing in the Value column)

[DebuggerDisplay("x = {x} y = {y}")]

Used on a type with fields x and y.

x = 5 y = 18

[DebuggerDisplay("String value is {getString()}")]Parameter syntax can vary between languages. Therefore, use it with care.

String value is [5, 6, 6]

DebuggerDisplay can also accept named parameters.

Parameters

Purpose

Name, Type

These parameters affect the Name and Type columns of the variable windows. (They can be set to strings using the same syntax as the constructor.)Overusing these parameters, or using them incorrectly, can cause confusing output.

Target, TargetTypeName

Specifies the target type when the attribute is used at the assembly level.

Note Note

The autoexp.cs file uses the DebuggerDisplay attribute at the assembly level. The autoexp.cs file determines the default expansions Visual Studio uses for C# variables. You can examine the autoexp.cs file for examples of how to use the DebuggerDisplay attribute, or you can modify and compile the autoexp.cs file to change the default expansions. Be sure to back up the autoexp.cs file before you modify it. You must reference Microsoft.VisualStudio.DebuggerVisualizers.dll in \Program Files\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies. You can find the autoexp.cs file in My Documents/Visual Studio 2012/Visualizers.

Although you can use a general expression between the braces in a DebuggerDisplay attribute, this practice is not recommended.

A general expression in DebuggerDisplay has implicit access to the this pointer for the current instance of the target type only. The expression has no access to aliases, locals, or pointers. If the expression references properties, attributes on those properties are not processed. For example, the C# code [DebuggerDisplay("Object {count - 2}" would display Object 6 if the field count was 8.

Using expressions in DebuggerDisplay can lead to the following issues:

  • Evaluating expressions is the most expensive operation in the debugger and the expression is evaluated each time it is displayed. This can cause performance issues in stepping through code. For example, a complex expression that is used to display the values in a collection or list can be very slow when the number of elements is large.

  • Expressions are evaluated by the expression evaluator of the language of the current stack frame and not by the evaluator of the language in which the expression was written. This can cause unpredictable results when the languages are different.

  • Evaluating an expression can change the state of the application. For example, an expression that sets the value of a property mutates the property value in the executing code.

One way to reduce the possible problems of expression evaluation is by creating a private property that performs the operation and returns a string. The DebuggerDisplay attribute can then display the value of that private property. The following example implements this pattern:

[DebuggerDisplay("{DebuggerDisplay,nq}")]public sealed class MyClass {    public int count { get; set; }    public bool flag { get; set; }    private string DebuggerDisplay {        get{ return string.Format("("Object {0}", count - 2); }    }}

The following code example shows how to use DebuggerDisplay, together with DebuggerBrowseable and DebuggerTypeProxy. When viewed in a debugger variables window, such as the Watch window, it produces an expansion that looks like this:

Name

Value

Type

Key

"three"

object {string}

Value

3

object {int}

[DebuggerDisplay("{value}", Name = "{key}")]
internal class KeyValuePairs
{
    private IDictionary dictionary;
    private object key;
    private object value;
    public KeyValuePairs(IDictionary dictionary, object key, object value)
    {
        this.value = value;
        this.key = key;
        this.dictionary = dictionary;
    }

    public object Key
    {
        get { return key; }
        set
        {
            object tempValue = dictionary[key];
            dictionary.Remove(key);
            key = value;
            dictionary.Add(key, tempValue);
        }
    }

    public object Value
    {
        get { return this.value; }
        set
        {
            this.value = value;
            dictionary[key] = this.value;
        }
    }
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable
{
    public Hashtable hashtable;

    public MyHashtable()
    {
        hashtable = new Hashtable();  
    }    private string DebuggerDisplay    {        get { return "Count = " + hashtable.Count); }    }

    private class HashtableDebugView
    {
        private MyHashtable myhashtable;
        public HashtableDebugView(MyHashtable myhashtable)
        {
            this.myhashtable = myhashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Keys
        {
            get
            {
                KeyValuePairs[] keys = new KeyValuePairs[myhashtable.hashtable.Count];

                int i = 0;
                foreach (object key in myhashtable.hashtable.Keys)
                {
                    keys[i] = new KeyValuePairs(myhashtable.hashtable, key, myhashtable.hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    }
}
Show:
© 2014 Microsoft