1

Let's imagine some dictionary:

Dictionary<string, string> _dict = new Dictionary<string, string>

When I log ValueCollection from the dictionary to the console the output is:

System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.String]

But if I run the next command in the Immediate window during debugging:

_dict.Values

Then the output is pretty detailed:

Count = 3
[0]: "value1"
[1]: "value2"
[2]: "value3"

How can I make the output to the console window the same as in the Immediate window without writing my own output handler?

AndiS
  • 63
  • 6
  • 1
    Does this answer your question? [A way to pretty print a C# object](https://stackoverflow.com/questions/6309254/a-way-to-pretty-print-a-c-sharp-object) – Thomas Weller Oct 19 '20 at 21:08
  • Why would you want do this if you have a debugger with an immediate window? – Thomas Weller Oct 19 '20 at 21:11
  • Consider using a structured logging framework like Serilog, instead of just logging to the console directly. – StriplingWarrior Oct 19 '20 at 21:14
  • Because It would be nice to write the contents of a collection to the log file in Debug level. This is actually what I need now. BTW, the main question here is if there is some existing .NET library that Immediate window uses and that I can use also? – AndiS Oct 19 '20 at 21:16
  • 1
    This is a great question and I wonder why are people questioning why would you want this. Of course you want a human readable output on the debugger. – JAlex Oct 19 '20 at 21:28

2 Answers2

2

You have to write your own, just like the authors of the immediate window did, but the good news is that it isn't that hard.

All we have to do is create a string that says "Count = " followed by the Count, then we can output each item using the override of Select that includes the index, so we can format the output with both the index and value for each item.

We can even make it an extension method, so it becomes part of the Values property:

public static class Extensions
{
    public static string GetDetailedString<TKey, TValue>(
        this Dictionary<TKey, TValue>.ValueCollection input)
    {
        var output = $"Count = {input.Count}";

        if (input.Count > 0)
            output += Environment.NewLine + "    " +
                      string.Join(Environment.NewLine + "    ",
                          input.Select((item, index) => $"[{index}]: \"{item}\""));

        return output;
    }
}

If you want to extend this to the KeyCollection as well, then we might as well extend it to List<T> and create a few overloads. Heck, while we're at it, we may as well include an overload for the Dictionary itself:

public static class Extensions
{
    public static string GetDetailedString<TKey, TValue>(
        this Dictionary<TKey, TValue>.KeyCollection keys)
    {
        return keys.ToList().GetDetailedString();
    }

    public static string GetDetailedString<TKey, TValue>(
        this Dictionary<TKey, TValue>.ValueCollection values)
    {
        return values.ToList().GetDetailedString();
    }

    public static string GetDetailedString<T>(this IList<T> input)
    {
        var output = $"Count = {input.Count}";

        if (input.Count > 0)
            output += Environment.NewLine + "    " +
                      string.Join(Environment.NewLine + "    ",
                          input.Select((item, index) => $"[{index}]: \"{item}\""));

        return output;
    }

    public static string GetDetailedString<TKey, TValue>(
        this Dictionary<TKey, TValue> dict)
    {
        var output = $"Count = {dict.Count}";

        if (dict.Count > 0)
            output += Environment.NewLine + "    " +
                      string.Join(Environment.NewLine + "    ",
                          dict.Select((item, index) => 
                              $"[{index}]: {{[{item.Key}, {item.Value}]}}"));

        return output;
    }
}

In use this might look something like:

Dictionary<string, string> _dict = new Dictionary<string, string>
{
    { "key1", "value1"},
    { "key2", "value2"},
    { "key3", "value3"},
};

Console.WriteLine("Dictionary:");
Console.WriteLine(_dict.GetDetailedString());
Console.WriteLine("\r\nKeys:");
Console.WriteLine(_dict.Keys.GetDetailedString());
Console.WriteLine("\r\nValues:");
Console.WriteLine(_dict.Values.GetDetailedString());

Output

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
0

You can hand roll a function pretty easily to do this:

var i = 0;
var dictString = $"Count: {dict.Count}\n";
foreach (var val in dict.Values)
    dictString += $"[{i++}]: \"{val}\"\n";
Colin
  • 4,025
  • 21
  • 40
  • You wouldn't happen to be a Python programmer by any chance? The lack of braces and relying on indentation hinted at this. Also in the .NET world it is recommended to use `Envirnoment.NewLine` instead of `\n` or `\r\n` or whatever is needed. – JAlex Oct 20 '20 at 12:43
  • I've been programming .NET for almost 20 years and one of the things I most value is its ability for clean, readable code. I ALWAYS recommend removing curly braces where they aren't needed (like for a single statement in a for loop) as they simply add visual noise/cognitive load. It's actually a language feature that doesn't rely on indentation, but indentation boosts readability. Similarly for \n vs. Environment.NewLine, the former is more concise, readable, and is supported in the Console, which is where the OP is printing output. – Colin Oct 20 '20 at 14:44