What’s New in C# 10: New Possibilities for Validation and Logging Methods

This is part of a series on the new features introduced with C# 10.

From C# 10 we can make use of the [CallerArgumentExpression] attribute.

This attribute can be applied to a parameter to get information about another specified parameter in the method.

Take the following method as an example:

static bool ValidInput(string? inputFromUser,
                string inputDataName,
                bool validationCondition,
                out string? validationErrorMessage,
                [CallerArgumentExpression("validationCondition")] string? validationConditionText = null)
{
    if (validationCondition)
    {
        validationErrorMessage = null;
        return true;
    }

    validationErrorMessage = $"input '{inputFromUser ?? "null"}' from user for {inputDataName} is invalid because '{validationConditionText}'";
    return false;
}

In this method the validationConditionText argument has the [CallerArgumentExpression] applied.

When you use the [CallerArgumentExpression] attribute you need to supply a single constructor parameter. This is a string parameter that specifies which of the other parameters we want to capture information about. In this case it’s the bool validationCondition parameter.

We could make use of this method in a console application:

using System.Runtime.CompilerServices;
using static System.Console;

WriteLine("Please enter your user name");
string? userName = ReadLine();

WriteLine("Please enter your age");
string? age = ReadLine();

string? validationErrorMessage;

if (!ValidInput(userName,
                inputDataName: "user name",
                validationCondition: userName is not null,
                validationErrorMessage: out validationErrorMessage))
{
    WriteLine(validationErrorMessage);
}

if (!ValidInput(inputFromUser: age,
                inputDataName: "age",
                validationCondition: age is not null && int.TryParse(age, out _),
                validationErrorMessage: out validationErrorMessage))
{
    WriteLine(validationErrorMessage);
}

ReadLine();

Every time we call the ValidInput method, we pass a Boolean expression that needs to be satisfied for the input to be recognized as valid, for example: userName is not null.

If we ran the console app and entered a null for user name and some non numeric input for age:

Please enter your user name
^Z
Please enter your age
aaa
input 'null' from user for user name is invalid because 'userName is not null'
input 'aaa' from user for age is invalid because 'age is not null && int.TryParse(age, out _)'

Notice the two validation error messages output contain the boolean expression used in the source code: userName is not null and age is not null && int.TryParse(age, out _).

The [CallerArgumentExpression] attribute pulls out those expressions and lets us access them as strings to be used at runtime.

This kind of user validation is not the primary intended use case for this attribute as telling an end user 'age is not null && int.TryParse(age, out _)' is not very helpful or user friendly, however the example above illustrates the possibilities. This approach could still be used with a more generic error message given to the user and a more detailed one written to logs/traces. The Microsoft documentation states: “Diagnostic libraries may want to provide more details about the expressions passed to arguments. By providing the expression that triggered the diagnostic, in addition to the parameter name, developers have more details about the condition that triggered the diagnostic. That extra information makes it easier to fix.”

If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.

SHARE:

Add comment

Loading