Versatile string validation I passed a technical test the other day, one part included a cheap string validation and I am wondering this can be further improved to be more versatile to requirement changes.

The requirements were something like that 


  Create a Validate method, which accepts a string and returns true if it's valid and false if it's not.
  
  A string is valid if it satisfies the rules below:
  
  
  The string must be at least 6 characters long and not exceed 16 characters.
  The string must contain only letters, numbers and optionally one hyphen (-).
  The string must start with a letter, and must not end with a hyphen.
  For example, validate("Michelle Belle"); would return false because it contains a space.
  


My solution was like that:

public static class ComparableExtensions
{
    public static bool IsStrictlyLowerThan&lt;TComparable&gt;(this TComparable comparable, TComparable value)
        where TComparable : IComparable&lt;TComparable&gt;
    {
        return comparable.CompareTo(value) &lt; 0;
    }

    public static bool IsStrictlyGreaterThan&lt;TComparable&gt;(this TComparable comparable, TComparable value)
        where TComparable : IComparable&lt;TComparable&gt;
    {
        return comparable.CompareTo(value) &gt; 0;
    }

    public static bool IsStrictlyNotBetween&lt;TComparable&gt;(this TComparable comparable, TComparable lowerBound, TComparable upperBound)
        where TComparable : IComparable&lt;TComparable&gt;
    {
        if (lowerBound.IsStrictlyGreaterThan(upperBound))
        {
            throw new ArgumentOutOfRangeException(nameof(lowerBound) + nameof(upperBound));                
        }

        return comparable.IsStrictlyLowerThan(lowerBound) || comparable.IsStrictlyGreaterThan(upperBound);
    }
}

public static class CharExtensions
{
    public static bool IsLetterOrDigit(this char c)
    {
        return char.IsLetterOrDigit(c);
    }

    public static bool IsLetter(this char c)
    {
        return char.IsLetter(c);
    }

    public static bool IsHyphen(this char c)
    {
        return c == '-';
    }
}

public class Test
{
    public static bool Validate(string str)
    {
        if (str.Length.IsStrictlyNotBetween(6, 16))
        {
            return false;
        }

        if (!str.First().IsLetter() || str.Last().IsHyphen())
        {
            return false;
        }

        var hyphenCount = 0;

        for (var i = 1; i &lt; str.Length - 1; i++)
        {
            if (str[i].IsLetterOrDigit())
            {
                continue;
            }
            if (str[i].IsHyphen())
            {
                hyphenCount++;
                if (hyphenCount &gt; 1)
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        return true;
    }
}


I purposefully decided to not go with Regular Expressions to keep the logic readable and I am wondering if my code can be further refactored to incorporate new business rules.
There's a bug: you're not checking if the last character is a letter or digit, only that it isn't a hyphen, so this fails to reject "abcdef&amp;". Denis' solution may be less efficient (2 iterations instead of 1), but with at most 16 characters that's not much of a concern, and it's both easier to read and it works correctly.
The first two rules read very nicely. I especially like that the business rules are translated to code almost 1:1, that will make updating easier. However, I do think those extension methods are over-engineered. str.Length &lt; 6 || str.Length &gt; 16 and !char.IsLetter(str.First()) || str.Last() == '-' is already quite readable, and that doesn't require extra code that needs to be understood and maintained.
You can use =&gt; syntax for methods with single-expression bodies.