There’s several debugging types of attributes I use fairly regularily, such as DebuggerStepThrough and DebuggerDisplay, but I decided to take a look at some of the other, similar, attributes to see if I was missing anything, here’s what I found out.
Introduction
At the time of writing these are the debugging attributes I found
- DebuggerStepThrough
- DebuggerDisplay
- DebuggerNonUserCode
- DebuggerHidden
- DebuggerBrowsable
- DebuggerTypeProxy
- DebuggerStepperBoundary
Before I get into the specific attributes, I will be listing the attribute targets that the attribute may be applied to, when it says an attribute may be set on a method, this also includes the setter/getter (methods) on a property. But not the property itself, this requires the AttributeTargets.Property target to be set.
DebuggerStepThrough
References
DebuggerStepThroughAttribute Class
Usage
- Class
- Struct
- Constructor
- Method
Synopsis
Let’s start with the one I used a lot in the past (on simple properties, i.e. equivalent of auto-properties). Placing a DebuggerStepThrough on any of the usage targets, will inform the debugger (in Visual Studio, not tested in Xamarin Studio as yet) to simply step through (or in debugging terms, step over) the code. In other words the debugger will not enter a method call.
This is especially useful for situations where, maybe you have simple code, such as properties which just return or set a value and this will save you stepping into this property setters or getters during a debugging session.
Example
public class MyClass { private int counter; [DebuggerStepThrough] public int Increment() { return counter++; } }
As stated in the usage, you can also apply this to the class itself to stop the debugger stepping into any code in the class. Useful if the class is full of methods etc. which you don’t need to step into.
DebuggerDisplay
Next up, is the DebuggerDisplayAttribute.
References
DebuggerDisplayAttribute Class
Usage
- Assembly
- Class
- Struct
- Enum
- Property
- Field
- Delegate
Synopsis
In situations where you’re debugging and you place the mouse pointer over the an instance of MyClass (as defined previously) you’ll see the popup say something like
{MatrixTests.MyClass}
next to the variable name. This is fine if all you want is the type name or if you want to drill into the instance to view fields or properties but if you’ve got many fields or properties, it might be nicer if the important one(s) are displayed instead of the type name.
Before we delve into DebuggerDisplay, if your class implements a ToString override method then this will normally be displayed by the debugger. If you’re using this for something else, then it will not fulfill the requirements for displaying something different when the instance is hovered over. But if this ToString has everything you need then there’s no need for a DebuggerDisplay attribute.
Example
Let’s start by seeing the ToString and DebuggerDisplay that would be equivalent
public override string ToString() { return $"Counter = {counter}"; } // equivalent to [DebuggerDisplay("Counter = {counter}")] public class MyClass { // code }
Both of the above will show debugger popup of “Counter = 0” (obviously 0 is replaced with the current counter value).
As you can see within the DebuggerDisplayAttribute, we can include text and properties/fields we just need to enclose them in {}. We can also use methods, within the DebuggerDisplay. Let’s assume we have a private method which creates the debugger output
[DebuggerDisplay("{DebuggerString()}")] public class MyClass { private string DebuggerString() { return $"Counter = {counter}"; } // other code }
As stated previously these all show a debugger display of “Counter = 0” – they also display quotes around the text. We can remove these by specifying nq (nq == no quotes), as per
[DebuggerDisplay("{DebuggerString(), nq}")]
So now the output display is Counter = 0 i.e. without quotes.
You can, ofcourse, extend format to display more fields/properties as per C# 6.0 string interpolation. i.e. if we have a matrix class with two properties, Rows and Columns and want to display these in the debugger display we can use
[DebuggerDisplay("Rows = {Rows}, Columns = {Columns}")]
We can also extend this syntax to use basic expressions, for example
[DebuggerDisplay("Counter = {counter + 10}")]
the above code will evaluate the counter field and then add 10 (as I’m sure you guessed).
Gotcha’s
Just remember that whatever you place in the DebuggerDisplay will be executed when you view it. So if you’re calling a remote service or database, be ready for the performance hit etc.
DebuggerNonUserCode
References
DebuggerNonUserCodeAttribute Class
Using the DebuggerNonUserCode Attribute in Visual Studio 2015
Change Debugger behavior with Attributes
Usage
- Class
- Struct
- Constructor
- Method
- Property
Synopsis
This is very similar to DebuggerStepThrough, but as you can see from the usage, also allows us to apply this attribute to a property as a whole.
Examples
Used in the same way as DebuggerStepThrough, see those examples.
DebuggerHidden
References
DebuggerHiddenAttribute Class
Using the DebuggerNonUserCode Attribute in Visual Studio 2015
Change Debugger behavior with Attributes
Usage
- Constructor
- Method
- Property
Synopsis
This is very similar to DebuggerStepThrough and DebuggerNonUserCode, but as you can see from the usage it’s somewhat restricted on what it can be applied to.
Examples
Used in the same way as DebuggerStepThrough, see those examples.
DebuggerBrowsable
References
DebuggerBrowsableAttribute Class
Usage
- Property
- Field
Synopsis
If you are viewing an instance of the MyClass (shown previously) within the popup debug window (displayed when you hover over the instance variable during a Visual Studio debugging session), you will see a + sign to the left of the class, meaning you can view the fields/properties in this class, but in some situations you might want to reduce the fields/properties (or what might be seen as noise). This is especially useful in situations where maybe you have lots of fields or properties which are maybe of limited usefulness in a debugging scenario, or maybe pre auto properties, you might wish to “hide” all fields. Then simply mark the field or property with
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private int counter;
Now when you view the instance of MyClass, no + will appear as I’ve stipulated never display this field as browseable.
The other options for DebuggerBrowsableState are
- DebuggerBrowsableState.Collapsed
- DebuggerBrowsableState.RootHidden
as per Microsoft documentation…
Collapsed shows the element as collapsed whilst RootHidden does not display the root element; display the child elements if the element is a collection of array items.
Examples
// never show the field in the debugger popup // wtahc or auto windows [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int counter;
Basically RootHidden used on an array property (for example)
public int[] Counters { // }
will not display Counters, but will displace the array of elements.
DebuggerTypeProxy
References
Using DebuggerTypeProxy Attribute
Usage
- Assembly
- Class
- Struct
Synopsis
When we looked at the DebuggerDisplay we found we could reference fields/properties within the class and even use a limited set of expressions. But in some scenarios, such as the one where we added a DebuggerString to the class, it might be preferable to extract the debugger methods/properties etc. to ensure the readability and maintainability of the original class.
Examples
Let’s start by changing the MyClass code to this
[DebuggerTypeProxy(typeof(MyClassDebuggerProxy))] public class MyClass { private int counter; public int Counter => counter; public int Increment() { return counter++; } }
I’ve added a property Counter, to allow us to use the counter in our proxy
Now create a simple proxy that looks like this
public class MyClassDebuggerProxy { }
If you now try to view the fields on an instance of MyClass the debugger will use the MyClassDebuggerProxy and in this instance there are no fields/properties and therefore nothing will be displayed (well they will if you expand Raw View but not at the top level in the debugger). So in this form, it’s a simple way to hide everything from the debugger – if that is required. Let’s be honest that’s not usually the intention, so let’s add some properties to the proxy and use the MyClass.Counter property.
public class MyClassDebuggerProxy { private MyClass _instance; public MyClassDebuggerProxy(MyClass instance) { _instance = instance; } public int DebuggerCounter { get { return _instance.Counter; } } }
Now when you debug an instance of MyClass, the debugger will display the property DebuggerCounter from the proxy class. The _instance field will not be displayed. This example is a little pointless, but unlike the limited expression capability of DebuggerDisplay, the full capabilities of .NET can be used to add more data to the debugger, or change debugger displayed data.
Note: If you want to display something other than the type for the instance of MyClass, then you’ll still need to apply a DebuggerDisplay attribute to the MyClass object, not to the proxy as this will be ignored.
DebuggerStepperBoundary
References
DebuggerStepperBoundaryAttribute Class
Usage
- Constructor
- Method
Synopsis
This is a slightly odd concept (well it is to me at the moment). Using this attribute on a method (say our Increment method in MyClass) will in essence tell the debugger to switch from stepping mode to run mode. In other words, if you have a breakpoint on a call to myClass.Increment then press F10 or F11 (to step over or step in to the Increment method) the debugger will see the DebuggerStepperBoundary and instead do the equivalent of F5 (or run). From this perspective the use of such an attribute seems fairly limited, however the Microsoft documentation (shown in the references above) states…
“When context switches are made on a thread, the next user-supplied code module stepped into may not relate to the code that was in the process of being debugged. To avoid this debugging experience, use the DebuggerStepperBoundaryAttribute to escape from stepping through code to running code.”