Search code examples
c#string-interpolation

c#: interpolated string to printout variable name and value


Is there a shorter way to print out a variable name and its value in C#? What i do now is:

int myvar = 42;
Console.WritelLine($"{nameof(myvar)}={myvar}"); // or any other log function
//myvar=42

(I have been using Python a lot lately and really like pythons f"{myvar=}", which does exactly that.)

Thanks for your help


Solution

  • Yes-and-No.

    No, you can't capture local variable names as simply as how Python does it.

    ...but it is possible by using a helper method with CallerArgumentExpression - this does require C# 10.0 and .NET 6+ - you also need to add [assembly: EnableCallerArgumentExpression] in your AssemblyInfo.cs.

    Like so:

    public static class MyExtensions
    {
        public static String Dump<T>( this T value, [CallerArgumentExpression(nameof(value))] String? name = null )
        {
            String valueAsText;
            if( value == null )
            {
                valueAsText = "null";
            }
            else if( value is IEnumerable e ) // Can't use IEnumerable<T> (unless you use MethodInfo.MakeGenericMethod, which is overkill)
            {
                valueAsText = "{ " + e.Cast<Object?>().Select( i => i?.ToString() ).StringJoin() + " }";
            }
            else
            {
                valueAsText = '"' + value.ToString() + '"';
            }
    
            name = name ?? "(Unnamed expression)";
    
            return name + " := " + valueAsText;
        }
    
        public static String StringJoin( this IEnumerable<String?> collection, String separator = ", " )
        {
            return String.Join( separator: separator, collection.Select( s => s is null ? "null" : ( '"' + s + '"' ) ) );
        }
    }
    

    The Dump<T> method above is generic (over T) instead of using this Object? value to avoid unnecessary boxing of value when T is a value-type.

    Used like so:

    int myvar = 42;
    Console.WriteLine( myvar.Dump() );
    // myvar := "42"
    
    Int32?[] arr = new[] { 1, 2, 3, (Int32?)null, 5 };
    Console.WriteLine( arr.Dump2() );
    // arr := { "1", "2", "3", null, "5" }
    

    Screenshot proof:

    enter image description here

    (I had to name it Dump2 in the above screenshot because Dump is already defined by Linqpad).