Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 9 records validation

Tags:

With the new record type of C# 9, how is it possible to inject a custom parameter validation/ null check/ etc during the construction of the object without having to re-write the entire constructor?

Something similar to this:

record Person(Guid Id, string FirstName, string LastName, int Age)
{
    override void Validate()
    {
        if(FirstName == null)
            throw new ArgumentException("Argument cannot be null.", nameof(FirstName));
        if(LastName == null)
            throw new ArgumentException("Argument cannot be null.", nameof(LastName));
        if(Age < 0)
            throw new ArgumentException("Argument cannot be negative.", nameof(Age));
    }
}
like image 346
Simon Mattes Avatar asked Nov 11 '20 10:11

Simon Mattes


Video Answer


2 Answers

If you can live without the positional constructor, you can have your validation done in the init portion of each property which requires it:

record Person
{
    private readonly string _firstName;
    private readonly string _lastName;
    private readonly int _age;
    
    public Guid Id { get; init; }
    
    public string FirstName
    {
        get => _firstName;
        init => _firstName = (value ?? throw new ArgumentException("Argument cannot be null.", nameof(value)));
    }
    
    public string LastName
    {
        get => _lastName;
        init => _lastName = (value ?? throw new ArgumentException("Argument cannot be null.", nameof(value)));
    }
    
    public int Age
    {
        get => _age;
        init =>
        {
            if (value < 0)
            {
                throw new ArgumentException("Argument cannot be negative.", nameof(value));
            }
            _age = value;
        }
    }
}

Otherwise, you will need to create a custom constructor as mentioned in a comment above.

(As an aside, consider using ArgumentNullException and ArgumentOutOfRangeException instead of ArgumentException. These inherit from ArgumentException, but are more specific as to the type of error which occurred.)

(Source)

like image 81
Knowledge Cube Avatar answered Sep 20 '22 17:09

Knowledge Cube


The following achieves it too and is much shorter (and imo also clearer):

record Person (string FirstName, string LastName, int Age, Guid Id)
{
    private bool _dummy = Check.StringArg(FirstName)
        && Check.StringArg(LastName) && Check.IntArg(Age);

    internal static class Check
    {
        static internal bool StringArg(string s) {
            if (s == "" || s == null) 
                throw new ArgumentException("Argument cannot be null or empty");
            else return true;
        }

        static internal bool IntArg(int a) {
            if (a < 0)
                throw new ArgumentException("Argument cannot be negative");
            else return true;
        }
    }
}

If only there was a way to get rid of the dummy variable.

like image 24
Batox Avatar answered Sep 21 '22 17:09

Batox