在 DebuggerDisplay 中是否可以使用条件?

30

考虑以下类:

[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; }
}

我想基于IsTempFileName属性添加一个调试器显示。当实例为临时文件时,我想添加字符串,TempFileName = {TempFileName,nq}。如何实现这样的功能?


1
要不直接添加一个返回所需内容的方法怎么样?个人而言,我通常会重写 ToString 方法,但你也可以添加其他方法,并将其用作调试器字符串。 - CodesInChaos
2
可以这样做,但那只能是最后的选择,因为该方法会向类引入仅用于调试的特性。这种做法有点像糟糕的设计。我从不为了调试而覆盖 ToString() 方法。 - Kees C. Bakker
4个回答

38

您可以使用条件运算符(?:)

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

IsTempFile == false

enter image description here

IsTempFile == true

在此输入图片描述


2
哇!你值得一瓶啤酒!(刚刚去掉逗号前的空格。)你有关于这个的任何文档吗? - Kees C. Bakker
@KeesC.Bakker 这只是作为表达式的一部分使用?运算符 - Justin
我不知道我们可以在这些“神奇”的字符串中做那样的事情。 - Kees C. Bakker

11

您可以使用任何有效的表达式。

但请记住,调试器会频繁地评估这些表达式,因此您让它们变得越复杂,就会越容易注意到调试速度减慢(例如,在代码步进时)。

另一个需要考虑的主要问题是,表达式是由使用类的语言的调试器评估的。如果该类及其所有潜在用户都在C#中,那么就没有问题,您可以使用像三元运算符之类的东西。但是,如果您的类还将用于另一种语言,则:

  1. 不能保证调试器甚至会使用[DebuggerDisplay]属性,
  2. 如果它确实使用了,也不能保证它会尝试评估{expression}块,以及
  3. 如果您开始做任何花哨的事情(比如使用?:),它很可能无法评估您的C#表达式

最安全的方法是添加一个私有属性来计算调试器值:

[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;
    }
  }

}

这是一个私有属性,因此不会妨碍任何潜在的子类。


1
你可以使用扩展方法来使用它。
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());
        }
    }
}

{{链接1:观看}}


1
首先,在我之前的答案中,先点赞“sloth”的答案…因为他们让我朝着正确的方向前进了。
第二,这里有一篇文章:

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

以下是文章名称和作者,以防上面的链接将来失效。
自定义 Visual Studio 调试器中的对象显示方式
Leslie Richardson
Visual Studio 调试和诊断程序经理
第三个例子是基于空或非空子集合的稍微通用一些的示例:
[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; }
}

请注意,当消费代码为Visual Basic(或其他语言不支持null == MyKids ? 0 : MyKids.Count表达式时),您的第三个示例将会出错。您链接的文章甚至特别提到了这一点。 - Zastai
@Zastai 感谢您提供可能存在的陷阱说明。 - granadaCoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接