Equipping 3rd party types with debugging capabilities

1/13/2025
2 minute read

Debugging is important, and it's often useful to be able to inspect the state of objects in the debugger. However sometimes you're working with 3rd party types that don't have any debugging capabilities, so you can't see their internal state easily. In this blog post we will have a look on how to equip 3rd party types with debugging capabilities.

DebuggerDisplayAttribute to the rescue

The DebuggerDisplayAttribute is a very useful attribute that allows you to specify how an object should be displayed in the debugger. "Equipping" your own types as such:

[DebuggerDisplay("Name = {Name}, Age = {Age}")]
public class Person
{
    public required string Name { get; set; }
    public required int Age { get; set; }
}

var person = new Person { Name = "John", Age = 42 };

This will make the debugger display the Person object as Name = John, Age = 42. Cool, cool, cool. But what if you're working with a 3rd party type where you can't easily add the DebuggerDisplayAttribute? Well, what if I tell you, that you can?

Equipping 3rd party types with DebuggerDisplayAttribute

There is a lesser known property called Target which defines, which type the DebuggerDisplayAttribute should be applied to. We can use this in combination with the assembly attribute to equip any type with debugging capabilities. For the sake of keeping it easy, I will equip the Task type even though it already has debugging capabilities.

[assembly: DebuggerDisplay("Is Task finished: {IsCompleted}", Target = typeof(Task))]

var task = Task.Delay(100_000);
await task;

Now, hovering over the task object will result into:

Debugging

There are some "natural" downsides to this approach:

  • You can only use public types or interfaces for obvious reasons.
  • It might be not obvious where the debugging information is coming from. Especially if you have a deep dependency graph and someone in the middle is equipping a type with debugging capabilities.

DebuggerTypeProxy - Displaying complex states in the debugger

With the DebuggerTypeProxy we have the possibility to show more complex states. This can be helpful in certain cases where an object for example has a lot of properties.

Equip 3rd party types with a deconstructor

Deconstructor are a C# language feature that allows you to define a method that will be called when an object is being split up into its components. While this is straightforward to implement for your own types, it is not possible to add a deconstructor to a 3rd party type - or is it?

Time abstraction in .NET 8

With the upcoming release of .NET, the team introduced an abstraction of time itself. That can bring you major benefits especially if you have to test scenarios where time is a crucial part! Until now, you had to create your own wrapper. This, of course, makes integration with 3rd party libraries tricky.

An error has occurred. This application may no longer respond until reloaded. Reload x