Search code examples
c#macroscode-generation

C# Make Assert Print Expression Being Asserted


When Debug.Assert fails, it shows a very unhelpful error: Assertion failed

This can be improved to get at least some information about the location of the error, like function name, file, and line number like this:

public static void MyAssert(bool expr,
    [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
    [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
    [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
    string message = $"Assertion failed in {memberName} ({sourceFilePath}:{sourceLineNumber})";
    Debug.Assert(expr, message);
}

This helps, but what would be really cool was to be able to see the expression the caller wanted to assert, something like this:

public static void MyAssert(bool expr,
    [PreviousCallerArgumentAsString] string argument)  /* wish this worked */
{
    string message = $"Assertion '{argument}' failed";
    Debug.Assert(expr, message);
}

I am just transitioning to C# but in C++ this could be done using a macro like this:

#define ASSERT(Expr) Debug.Assert(Expr, "Assertion " #Expr " failed.")

ASSERT(1 + 1 == 3);
/* would expand to */
Debug.Assert(1 + 1 == 3, "Assertion 1 + 1 == 3 failed.");

In my code, I use asserts pretty liberally and having to retype the expression slows you down a ton.

Is there a way something like this could be achieved in C#?


Solution

  • You're in luck! This functionality was implemented in C# 10, using [CallerArgumentExpression].

    For example:

    public static class Debug
    {
        public static void Assert(bool condition, [CallerArgumentExpression("condition")] string message = null)
        {
            if (!condition)
            {
                Console.WriteLine($"Assert failed! {message}");
            }
        }
    }
    

    When used with:

    Debug.Assert(true == false);
    

    Prints:

    Assert failed! true == false

    See it on SharpLab.