Standard primitive types vs custom structs in public API I am writing a .NET library and have a lot of properties like this:

public ??? SomeProperty { get; set; }


where valid values are 0-15 (or from 00000000 to 00001111 in binary). So I should choose a proper type of the property. I see two options:

byte

I can use byte and insert value validation in each property setter:

public byte SomeProperty
{
    get { return _someValue; }
    set
    {
        if (value &gt; 15)
            throw new ArgumentOutOfRangeException(nameof(value), value, "Message.");

        _someValue = value;
    }
}


Advantages:


you don't need to cast literals from 0 to 15 to some another type.


Disadvantages:


value check must be inserted in each property setter;
property signature doesn't reflect valid values.


custom value type

Or I can create a struct like FourBitNumber handling invalid values in its constructor:

public struct FourBitNumber
{
    private readonly byte _someValue;

    public FourBitNumber(byte value)
    {
        if (value &gt; 15)
            throw new ArgumentOutOfRangeException(nameof(value), value, "Message.");

        _someValue = value;
    }
}


and use this struct as type for the property:

public FourBitNumber SomeProperty { get; set; }


Advantages:


user of the API takes care about passed values by casting them to FourBitNumber so the library always gets valid values;
property type clearly says what valid values are.


Disadvantages:


you need always cast values of primitive types to FourBitNumber which is inconvenient especially with literals.




At now the second way seems better for me but necessity of casting everywhere is really annoying. What other aspects should I take into an account to take final decision?
custom value type

I would use this becasue the disadvantage that you name 


  you need always cast values of primitive types to FourBitNumber which is inconvenient especially with literals.


can be easily overcome by implementing an implict operator for FourBitNumber to byte:

public static implicit operator byte(FourBitNumber value) =&gt; value._someValue;


and an explicit one for byte to FourBitNumber

public static explicit operator FourBitNumber(byte value) =&gt; new FourBitNumber(value);


so now it doesn't have any disadvanteges ;-)