The DebuggerDisplayAttribute is a powerful way to customize the way values are displayed at debug time. Instead of getting a simple type name display, interesting fields, properties or even custom strings can be surfaced to the user in useful combinations
[DebuggerDisplay("Student: {FirstName} {LastName}")]
public sealed class Student {
public string FirstName { get; set; }
public string LastName { get; set; }
}
The DebuggerDisplay attribute can customize the name, value and type columns in the debugger window. Each one can be customized using a string which can contain constant text or expressions to be be evaluated by the expression evaluator. The latter is designated by putting the expression inside {}’s (this greatly resembles String.Format)
This feature while very powerful and useful can also easily contribute negatively to the debugging experience when used improperly (mostly in the area of performance). After several years of working in this area and helping customers out with bugs I’ve come up with a few recommendations to help prevent this from happening [1]
Don’t use multiple functions or properties in the display string
Every time I see a DebuggerDisplay attribute like the following I cringe a little inside
[DebuggerDisplay("Student: {FirstName} {LastName} {Age} {Birthday} {Address}")]
Hands down the most expensive operation the expression evaluator does is evaluate a function. It dwarfs every other performance metric and can have a visible effect on stepping performance [2]. This is true for both functions and properties (as far as the debugger is concerned there is almost no difference between the two).
Every one of the expression holes above results in a property being evaluated. Each property must be evaluated individually and done so once for every instance of this type in every debugger display window. This set of evaluations is repeated on every single step. This can get very expensive if collections of this type end up getting displayed (imagine stepping with a couple thousand of these in the window!).
Please don’t read this and remove every property from DebuggerDisplay’s in your code. One property is very unlikely to cause a problem. Issues typically arise when many properties are used and collections of that type end getting displayed in the debugger windows.
Do use property / field names instead of language specific expressions
Evaluation holes in the string are not limited to just properties and function calls. They can handle pretty much any legal expression you can dream up. In the past this has lead to developers putting all manner of expressions into DebuggerDisplay attributes. The most common being the use of ternary expressions
[DebuggerDisplay("Count {IsEmpty ? 0 : Count}")]
While this works fine, please don’t do this! DebuggerDisplay attributes are evaluated not by the language in which they were defined but by the expression evaluator of the language in which they are being used. The above works great but only when viewed in a C# application. It fails miserably when viewed in other languages like VB.Net (and when F# has their own EE it will fail for that as well).
While there is no truly universal expression one which is supported by most languages is member names. Having an expression which is a simple property or field goes a long way to removing this problem.
Don’t evaluate expressions that throw exceptions
Earlier I mentioned that the most expensive action an expression evaluator performs is evaluating a function. The most expensive variant of evaluating a function are those which throw exceptions. Please don’t do this.
Don’t use mutating properties or functions
Usually this goes without saying but I’ve seen enough examples of this to warrant an entry. Don’t put expressions into DebuggerDisplay values which will mutate the underlying value. This will lead to only confusion.
Read more: jaredpar's WebLog
0 comments:
Post a Comment