December 4, 2012
If you already know and use the DebuggerDisplay
you can fast-forward to the evil stuff. Otherwise, keep reading to learn a new useful tool and also how not to use it.
Let’s start with a quick demonstration of when and how DebuggerDisplay
is useful. Let’s say you have some code like this:
public class Person
{
public string Name { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
}
Now, let’s say that you have a variable in your code that references an IEnumerable
. If you watch that value using the debugger, it can look like this:
We can see that there is a sequence of Employee
objects, but not much more. If we wanted to verify that there is an Employee
instance with the name “Sophie Jackson” we would need to expand each object to examine its Name
property which is a tedious task to say the least. This is where DebuggerDisplay
can help out. By decorating the Employee
class with the DebuggerDisplay
attribute, we can control how the debugger will display the object:
[DebuggerDisplay("Employee [Name = {Name}, Department = {Department}]")]
public class Employee : Person
{
public string Department { get; set; }
}
Now, if we check the value of the employees
variable as above, it will instead look like this:
Now it’s obviously a lot easier to find the employee called “Sophie Jackson”. Just browse through the list and you will quickly see whether that Employee
instance is there or not.
The debugger will evaluate the expression found in the DebuggerDisplay
attribute and show the value. Can this be used to do more complex things than to get a value from a property? Yes, it can. However, I urge you to resist the temptation to do fancy stuff here. The DebuggerDisplay
attribute can have unwanted side effects, as we will see further down in this article. Let’s look at an example:
[DebuggerDisplay("Employee [Name = {Name}, EmpYears = {((int)(System.DateTime.Now - EmploymentDate).TotalDays / 365)}]")]
public class Employee : Person
{
public string Department { get; set; }
public DateTime EmploymentDate { get; set; }
}
Again, watching the employees
variable in the debugger:
You can see how each object is presented with a number that is calculated based on the difference between the current date and EmploymentDate
, converted to years and cast to an int.
Now, while this is a very useful feature, it should be used with care. If we consume the class used in this example from code written in VB.NET this is what we get:
As you can see, the debugger now fails to evaluate the expression calculating the number of years. The reason is explained in the documentation:
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.
So instead of the evaluated expression, you get an error message. The same happens if you for instance have a typo in the property name:
As we can see, the DebuggerDisplay
attribute is very useful, but should be used to evaluate very simple and “cheap” values. Personally I only tend to use it to fetch values from property getters that just returns the value from a backing field with no other logic being involved. Above all, make sure to not have DebuggerDisplay
invoke any code that till mutate any state…
As we have seen from the above examples it’s not very hard to make the debugger fail to evaluate the expression in a DebuggerDisplay
attribute, but it does so in a nice and forgiving manner: it simply replaces the value placeholder with an error message instead of the evaluated expression. However, it is possible (even simple) to write an expression that instead will make you see this:
Now, that is not nice. I actually experience this in a project where this error started to appear among the developers and where we at first couldn’t identify what caused the environment to crash, since there (at first) were no obvious pattern of where the crash happened. After a while one of the team members found the offender. It was a DebuggerDisplay
attribute crafted like this:
[DebuggerDisplay("Employee [Name = {base.Name}, Department = {Department}]")]
public class Employee : Person
{
public string Department { get; set; }
public DateTime EmploymentDate { get; set; }
}
Note the base.Name
construct. This is what caused the debugger to crash. I found this somewhat surprising (perhaps even to be considered a bug in the debugger?), especially since it works well to use the construct this.Name
.