在C#Interactive中自定义打印对象

时间:2018-04-19 09:47:42

标签: c# .net c#-interactive

考虑这个MCVE类:

public class Foo<T>
{
    private readonly T value;

    public Foo(T value)
    {
        this.value = value;
    }
}

当我在 C#Interactive 中评估和打印此类对象时,它看起来像这样:

> new Foo<string>("bar")
Foo<string> { }

这没用。我希望它看起来像这样:

Foo<string> { "bar" }

我该怎么做?

我已尝试覆盖ToString这样:

public override string ToString()
{
    return $"Foo{typeof(T)} {{ {value} }}";
}

这并不能产生我想要的东西:

> new Foo<string>("bar")
[Foo<System.String> { bar }]

此输出至少有三个问题:

  1. 该值不在引号中。我希望它是"bar"而不是bar
  2. 类型参数显示为System.String,而不是string
  3. 整个值用方括号括起来。这是我最不关心的问题。
  4. 有没有办法让C#交互显示一个具有自定义格式的对象?

    我知道我可以在类中添加一个公共属性以显示值,但我不想因为封装问题而这样做。但要明确,这就是我的意思:

    public class Foo<T>
    {
        public Foo(T value)
        {
            Value = value;
        }
    
        public T Value { get; }
    }
    

    这打印得更接近我想要的:

    > new Foo<string>("bar")
    Foo<string> { Value="bar" }
    

    但是,正如我所写,我不想添加公共财产。

    如何使其表现如下?

    > new Foo<string>("bar")
    Foo<string> { "bar" }
    > new Foo<int>(42)
    Foo<int> { 42 }
    

    请注意,当使用除字符串之外的任何内容(例如int)时,应该没有引号。

3 个答案:

答案 0 :(得分:3)

您可以使用[DebuggerDisplay]属性自定义对象打印。除了覆盖ToString(),您还可以使用此属性中的任何方法/属性。例如:

[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
public class Foo<T>
{
    private readonly T value;

    public Foo(T value)
    {
        this.value = value;
    }

    private string GetDebuggerDisplay()
    {
        var val = value is string ? "\"" + value + "\"" : value?.ToString();
        return $"Foo<{typeof(T).Name.ToLower()}> {{ {val} }}";
    }
}

这可以避免重写ToString()或使用其他实现。比如返回T value的字符串表示。

您需要添加一个开关/案例来转换类名称,例如Int32int

nq属性的[DebuggerDisplay]部分会删除值周围的引号。

结果如下:

> new Foo<string>("bar")
Foo<string> { "bar" }

如需进一步参考,请查看Jared Parson关于[DebuggerDisplay]属性的优秀博文:https://blogs.msdn.microsoft.com/jaredpar/2011/03/18/debuggerdisplay-attribute-best-practices/

答案 1 :(得分:2)

如果您在第一次尝试时覆盖ToString()就好了,那么您可以在那里做任何您喜欢的事情。这可以让你更接近你想要的东西,你可以根据需要修改它:

public override string ToString()
{
    string v = value is string ? $"\"{value}\"" : $"{value}";
    string t = typeof(T).Name.ToLower();
    return $"Foo<{t}> {{ {v} }}";
}

答案 2 :(得分:1)

正如Kristian Hellang所说,通过在课堂上添加[DebuggerDisplay]属性可以简单轻松地解决这个问题:

[DebuggerDisplay("{ value }")]
public class Foo<T>
{
    private readonly T value;

    public Foo(T value)
    {
        this.value = value;
    }
}

这解决了所有问题:

> new Foo<string>("bar")
Foo<string>("bar")
> new Foo<int>(42)
Foo<int>(42)
> new Foo<DateTime>(new DateTime(2018, 4, 23))
Foo<DateTime>([23.04.2018 00:00:00])

渲染不使用花括号,而是使用构造函数指示的常规括号,但我只发现它是合适的。