Skip to content

Constrained primitive and string types #104

@erikbra

Description

@erikbra

To avoid using a string parameter everywhere the value should be a string, but not any string, it would be useful to make a constrained version of a string, which only allows certain values.

The same thing would be true for primitive types like ints, doubles, etc.

People keep building CustomerId, AccountNumber, ZipCode, AddressLine, SSN, etc types, which are all basically constrained strings. It is definitely possible to work around this by creating types like this:

    class CustomerId
    {
        readonly string _value;
        public CustomerId(string value)
        {
            Validate(value);
            _value = value;
        }

        private static void Validate(string value)
        {
            if (value.Length != 9)
            {
                throw new ArgumentOutOfRangeException(value, "CustomerId length must be 9.");
            }
        }

        public override string ToString()
        {
            return _value;
        }

        public static implicit operator CustomerId(string value)
        {
            return new CustomerId(value);
        }

        public static implicit operator string(CustomerId cid)
        {
            return cid._value;
        }
    }

And, of course generalize it into a ConstrainedString with a pure virtual Validate function.
However, it would be nice to have language support for this, as I see far too often people using strings when they don't accept any string. If we get language support for the concept, it would be easier to create domain types and get those checks compile-time, and not runtime.

Not sure of the best syntax, but one proposition would be to extend the generic syntax, like this:

    class CustomerId<T> where T: string { s => s.Length == 9 }
    {
    }
    class AccountNumber<T> where T: string { s => IsValidAccountNumber(s) }
    {
        public static bool IsValidAccountNumber(string s) {
            return (s % 11 == 0); // Arbitrary validation logic
        }
    }

and, similarily with primitive types:

    class Age<T> where T: int { i => i >= 0 &&  i < 130 }
    {
    }
    class AngleDeg<T> where T: double { d => d >= 0 &&  d <= 360 }
    {
    }

Etc, etc. This would make the language richer, and, maybe more checks could be performed compile-time (Roslyn to the rescue).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions