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 an
object[], 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?
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