30

Consider the following class:

[DebuggerDisplay("{GetType().Name,nq}: FileName = {FileName,nq}")]
public class FileWrapper
{
    public string FileName { get; set; }
    public bool IsTempFile { get; set; }
    public string TempFileName { get; set; }
}

I would like to add a debugger display based on the IsTempFileName property. I would like to add the string , TempFileName = {TempFileName,nq} when the instance is a temp file. How would I achieve something this?

Kees C. Bakker
  • 32,294
  • 27
  • 115
  • 203
  • 1
    How about simply adding a method that returns what you want? Personally I usually override `ToString`, but you can simply add some other method, and use it as the debugger string. – CodesInChaos Oct 15 '12 at 09:34
  • 2
    Could be done, but that would be kind of a last resort, because a method introduces features to the class that is only used for debugging. Something like that smells like a bad design. I never override a `ToString()` for debugging purposes. – Kees C. Bakker Oct 15 '12 at 10:09

4 Answers4

38

You can use the conditional operator (?:)

[DebuggerDisplay("{GetType().Name,nq}: FileName = {FileName,nq}{IsTempFile ? \", TempFileName: \" + TempFileName : System.String.Empty,nq}")]

IsTempFile == false

enter image description here


IsTempFile == true

enter image description here

sloth
  • 99,095
  • 21
  • 171
  • 219
11

You can use whatever expression is valid.

However, keep in mind that the debugger will evaluate these expressions a lot, so the more complicated you make them, the more you will start to noticed reduced debugging speed (e.g. when stepping through code).

Another major thing to consider is that the expression is evaluated by the debugger for the language using the class. If both the class and all its potential users are in C#, there is no problem and you can use things like the ternary operator. However, if your class is also to be used from another language, then:

  1. there's no guarantee the debugger will even use the [DebuggerDisplay] attribute at all,
  2. if it does, there's no guarantee that it will try to evaluate {expression} blocks, and
  3. there's a very good chance that it will fail to evaluate your C# expression if you start doing anything fancy (like using ?:)

The safest thing would be to add a private property to compute the debugger value:

[DebuggerDisplay("{DebugValue,nq}")]
public class FileWrapper {

  public string FileName     { get; set; }
  public bool   IsTempFile   { get; set; }
  public string TempFileName { get; set; }

  private string DebugValue {
    get {
      var text = string.Format("{0}: FileName={1}", this.GetType(), this.FileName);
      if (this.IsTempFile)
        text += string.Format(", TempFileName={0}", this.TempFileName);
      return text;
    }
  }

}

It's a private property, so it doesn't get in the way of any potential subclasses.

Zastai
  • 1,115
  • 14
  • 23
1

First, upvote "sloth" answer before mine....because they got me going on the right direction.

Second, Here is an article:

https://devblogs.microsoft.com/visualstudio/customize-object-displays-in-the-visual-studio-debugger-your-way/

Below is the name of the article and the author, in case the link above dies in the future.

Customize object displays in the Visual Studio debugger YOUR way

Leslie Richardson

Program Manager, Visual Studio Debugging & Diagnostics

Third, here is a slightly more generic example based on a null or not null child collection:

[System.Diagnostics.DebuggerDisplay("ParentName = '{ParentName}', MyKidsCount='{null == MyKids ? 0 : MyKids.Count}'")]
public class MyParent
{
    public string ParentName { get; set; }

    public ICollection<MyKid> MyKids { get; set; }
}
Community
  • 1
  • 1
granadaCoder
  • 26,328
  • 10
  • 113
  • 146
  • Note that your third example will break when the consuming code is Visual Basic (or some other language for which `null == MyKids ? 0 : MyKids.Count` is not a valid expression). The article you linked even specifically mentions this. – Zastai Feb 03 '20 at 12:02
  • @Zastai Thanks for the note about possible pitfall. – granadaCoder Feb 03 '20 at 13:22
1

You can use it with the Extensions method.

using System;
using System.Linq;
using System.Diagnostics;
using System.ComponentModel;

namespace ConsoleApplicationDebuggerDisplay
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObject o1 = new MyObject();
            MyObject o2 = new MyObject();
            o1.Items = new int[] { 1, 2, 3, 4 };
        }
    }

    [DebuggerDisplay("{DebuggerDisplay,nq}")]
    public class MyObject
    {
        [DebuggerDisplay("{Items.ToDebuggerDisplay(),nq}")]
        public int[] Items { get; set; }

        [DebuggerBrowsable(DebuggerBrowsableState.Never), Browsable(false)]
        internal string DebuggerDisplay
        {
            get
            {
                return string.Format("{{Items={0} ...}}"
                    , Items.ToDebuggerDisplay()
                    );
            }
        }
    }

    internal static class Extensions
    {
        public static bool IsNull(this object o)
        {
            return object.ReferenceEquals(o, null);
        }

        public static bool IsNotNull(this object o)
        {
            return !object.ReferenceEquals(o, null);
        }

        public static string ToDebuggerDisplay<T>(this System.Collections.Generic.IEnumerable<T> items)
        {
            if (items.IsNull())
                return "null";
            return string.Format("{{Count={0}}}", items.Count());
        }
    }
}

Watch