Search code examples
c#arraystypes

C# generic method detect if T is array and then index into the array


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?


Solution

  • 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