Another option would be to define your own data type to represent age values. This type could enforce the constraints.
struct Age
{
const int MinAge = 0;
const int MaxAge = 75;
readonly byte value;
public int Value { get { return value; } }
private Age(int value) {
this.value = (byte) Math.Max(MinAge, Math.Min(MaxAge, value));
}
public static implicit operator Age(int value) {
// Throw here if value is out of range, maybe?
return new Age(value);
}
public static implicit operator int(Age age) {
return age.value;
}
}
//usage:
Age childAge = 12; // 12
Age motherAge = 100; // 75
Edit: I would point out that it is generally considered bad practice to have "lossy" conversions exposed as implicit casts. I should have made the operator int(Age)
conversion explicit
instead. This would require writing an explicit cast Age age = (Age) 100;
which advertises to the consumer of the API that the cast isn't "identity preserving". This is similar to a cast from long
to int
or double
to float
, there is a loss of range/precision so the language requires you be explicit about it to demonstrate that you understand what you are doing.