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))
));
new Range<DateTime>(new DateTime(1), new DateTime(3)),
new Range<DateTime>(new DateTime(2), new DateTime(4))
new Range<DateTime>(new DateTime(2), new DateTime(4)),
new Range<DateTime>(new DateTime(1), new DateTime(3))
new Range<DateTime>(new DateTime(3), new DateTime(4)),
new Range<DateTime>(new DateTime(3), new DateTime(4))