samedi 12 novembre 2022

Is it possible to ensure struct fields are always valid?

I am trying to use a struct to store a musical note. The note is represented by a singular int, but it has lots of other fields that give other representations (note name, octave, ect.).

The problem is that a struct can be created without the constructor being called, meaning those fields are given the default values. These values are invalid for the note the struct is supposed to represent.

There are four options I can think of to go about this. (for these examples I have simplified what I'm trying to do):

The first way uses a parameterless constructor. This works, but the struct can still be created under some circumstances without the constructor being called.

readonly struct Number
{
    public readonly int Value;
    public readonly int ValueTimesTen;

    public Number()
    {
        this = new(1);
    }

    public Number(int value)
    {
        if (value is > 0 and <= 10)
        {
            Value = value;
            ValueTimesTen = Value * 10;
        }
        else
        {
            throw new ArgumentException("Number has the be between 1 and 10.");
        }
    }
}

The second way is to use properties the ensure the struct always give valid outputs. In this example some properties are recalculated everytime they are used. This is more costly in the real thing that I'm really trying to do.

readonly struct Number
{
    private readonly int _value;

    public int Value
    {
        get
        {
            if (GetValid(_value))
            {
                return _value;
            }
            else
            {
                return 1;
            }
        }
    }

    public int ValueTimesTen => Value * 10;

    private static bool GetValid(int number)
    {
        return (number is > 0 and <= 10);
    }

    public Number(int value)
    {
        if (GetValid(value))
        {
            _value = value;            
        }
        else
        {
            throw new ArgumentException("Number has the be between 1 and 10.");
        }
    }
}

The third way is to simply use a class. This doesn't feel right for something thats immutable and is really just a single number. But it might be the best option.

The fourth way is to simply allow the struct to have an invalid state, and to make sure I never allow this to happen elsewhere in my code.

Whats the best way to go about this?

Aucun commentaire:

Enregistrer un commentaire