Comparing ranges

Ryan Farley talks about comparing date ranges. In his post is this phrase “First range represented by r1start to r1end and second range represented by r2start to r2end”. Aha, a code smell! 2 things that are related should have that relationship represented in code.

It’s the Range pattern, which Fowler has discussed before. Here I get to present my attempt at it, using the latest in C# generics technology.

    class Range<T>

    {

        public readonly T Start;

        public readonly T End;

        public Range(T start, T end)

        {

            this.Start = start;

            this.End = end;

        }

    }

Wow, that’s exciting!

Now we need some comparison:

    static class Range

    {

        public static bool Overlap<T>(Range<T> left, Range<T> right)

            where T : IComparable<T>

        {

            if (left.Start.CompareTo(left.Start) == 0)

            {

                return true;

            }

            else if (left.Start.CompareTo(right.Start) > 0)

            {

                return left.Start.CompareTo(right.End) <= 0;

            }

            else

            {

                return right.Start.CompareTo(left.End) <= 0;

            }

        }

    }

The comparison algorithm is identical to Ryan’s, but written differently. First, because I can’t constrain type parameters to have comparison operators, I have to use IComparable<T>. Second, I broke out the expression to use if/else. I find that much easier to read & debug.

Note that I put the Overlap method in a new static class, instead of in Range<T>. That’s so I can write “Range.Overlap(x, y)” instead of “Range<int>.Overlap (x, y)” – the method will infer the type parameter all by itself.

 Here are the tests I used.

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(2)),

                new Range<DateTime>(new DateTime(1), new DateTime(2))

                ));

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(3)),

                new Range<DateTime>(new DateTime(2), new DateTime(4))

                ));

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(2), new DateTime(4)),

                new Range<DateTime>(new DateTime(1), new DateTime(3))

                ));

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(3), new DateTime(4)),

                new Range<DateTime>(new DateTime(1), new DateTime(2))

                ));

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(2)),

                new Range<DateTime>(new DateTime(3), new DateTime(4))

                ));