2

I'm trying to write a Dbg debug function that will print out some debug info about the given parameter and then return it. I would like arrays to be printed as the list of elements, and scalars to just be printed using .ToString(). So far I've got:


public static class Utils
{
    /// <summary>
    /// Log the given expression to the console iff a debugger is attached,
    /// returning that same value transparently. Useful for debugging values
    /// without rewriting all your code. Also logs the caller and line
    /// number via compiler trickery.
    /// </summary>
    public static T Dbg<T>(
        T thingToLog,
        // Ask the compiler to insert the current file, line number, and caller
        [CallerFilePathAttribute] string filepath = null,
        [CallerLineNumber] int lineNumber = 0,
        [CallerMemberName] string caller = null
    )
    {
        if (System.Diagnostics.Debugger.IsAttached)
        {
            string filename = filepath.Split('\\').Last();
            // FIXME This doesn't actually print the array, just "System.Byte[]" or similar
            string stringToLog = typeof(T).IsArray ? "[ " + String.Join(", ", thingToLog) + " ]" : thingToLog.ToString();
            Console.WriteLine($"[{filename}:{lineNumber} {caller}()] {stringToLog}");
        }
        return thingToLog;
    }
}

The problem is this line:

string stringToLog = typeof(T).IsArray ? "[ " + String.Join(", ", thingToLog) + " ]" : thingToLog.ToString();

which just outputs the type of thingToLog, like System.Byte[], but I want it to output the elements in the byte array. In the debugger, trying to access an element of thingToLog results in thingToLog[0] error CS0021: Cannot apply indexing with [] to an expression of type 'T', fair enough. But if I try to cast to an object[], then I get ((object[])thingToLog)[0] error CS0030: Cannot convert type 'T' to 'object[]'. And if I try first cast to an objectand then to anobject[], I get '((object[])((object)thingToLog))[0]' threw an exception of type 'System.InvalidCastException'`

Is it possible to detect if T is enumerable, and if so enumerate the elements for printing?

1
  • 3
    At the end you asked "Is it possible to detect if T is enumerable" which is a completely different problem from detecting whether T is an array? You can just check is IEnumerable for that. If you actually want to check for arrays, do you also want to check for 2-(or more) dimensional arrays? In that case, you cannot only use one single integer as the index.
    – Sweeper
    Commented Jul 8 at 7:23

1 Answer 1

6

To solve this problem, you can use reflection to handle different types of collections and arrays. I have provided the updated version of your 'Dbg' function which can properly handle arrays and other enumerable types.

using System;
using System.Collections;
using System.Linq;
using System.Runtime.CompilerServices;

public static class Utils
{
public static T Dbg<T>(
    T thingToLog,
    [CallerFilePath] string filepath = null,
    [CallerLineNumber] int lineNumber = 0,
    [CallerMemberName] string caller = null
)
{
    if (System.Diagnostics.Debugger.IsAttached)
    {
        string filename = filepath.Split('\\').Last();
        string stringToLog;

        if (thingToLog is IEnumerable enumerable && !(thingToLog is string))
        {
            var elements = enumerable.Cast<object>().Select(e => e?.ToString() ?? "null");
            stringToLog = "[ " + string.Join(", ", elements) + " ]";
        }
        else
        {
            stringToLog = thingToLog?.ToString() ?? "null";
        }

        Console.WriteLine($"[{filename}:{lineNumber} {caller}()] {stringToLog}");
    }
    return thingToLog;
}
}

Hope this will help

Not the answer you're looking for? Browse other questions tagged or ask your own question.