Alberto Gregorio

Home

C# Record Validation

Published May 27, 2022

Love them or hate them, C# records are here to stay, and you should learn to use them. Let’s delve a bit into my biggest problem with these guys. Starting with a simple example, let’s define a Person:

public record Person(string FirstName, string LastName, int Age);

What about validating that Person.Age? How would you go about that?

// this should not really be allowed, but nothing is getting validated!
Person alberto = new("Alberto", "Gregorio", -25);

One possibility would be getting rid of that positional constructor, define a couple of { get; init; } properties and validate in the explicit constructor, but that defeats the purpose of the record for certain scenarios.

After trying different approaches, I settled for this approach:

public record ValidatedPerson(string FirstName, string LastName, int Age) {

    private bool _validated = Check.ValidateFirstName(FirstName)
        && Check.ValidateLastName(LastName)
        && Check.ValidateAge(Age);

    internal static class Check {
        static internal bool ValidateFirstName(string firstName) {
            return string.IsNullOrEmpty(firstName) ? throw new ArgumentNullException(nameof(firstName)): true;
        }

        static internal bool ValidateLastName(string lastName) {
            return string.IsNullOrEmpty(lastName) ? throw new ArgumentNullException(nameof(lastName)): true;
        }

        static internal bool ValidateAge(int age) {
            return age < 0 ? throw new ArgumentOutOfRangeException(nameof(age)): true;
        }
    }
}


// Error: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'age')
var albert = new ValidatedPerson("Albert", "Einstein", -25);

So that is it for today, remember to validate your invariants, even using records!