C# – A property that can represent both a single date and a date range: How to properly model that

asp.net-mvccdesign-patternsinheritance

I work in a system that can represent a "shipping estimate" in two ways:

  1. A specific date: The item is guaranteed to ship at that date
  2. A day interval: The item will be shipped "X to Y" days from today

The information on the model is semantically the same, it is "the shipping estimate". When I get the information about the shipping estimate from the system, I can tell whether the estimate is of the first form or the second form.

The current model for this is similar to the following:

class EstimattedShippingDateDetails
{
    DateTime? EstimattedShippingDate {get; set;}
    Range? EstimattedShippingDayRange {get; set;}
}

Range is a simple class to wrap a "beginning -> end" of integers, somewhat like this:

struct Range
{
    int Start {get; set}
    int End {get; set}

    public override ToString()
    {
        return String.Format("{0} - {1}", Start, End);
    }
}

I don't like this approach because only one of the properties on the estimate model will ever be populated, and I need to test for null on one of them and assume the other one has the data.

Each of the properties is shown differently to the user but on the same spot on the UI, using a custom MVC DisplayTemplate, where the current switching logic resides:

@Model EstimattedShippingDateDetails

@if (Model.EstimattedShippingDate.HasValue)
{
    Html.DisplayFor(m => Model.EstimattedShippingDate)
}
else
{
    Html.DisplayFor(m => Model.EstimattedShippingDayRange)
}

How could I model this to make it more representative of the actual requirement while still keeping the display logic simple in an MVC application?

I thought about using an interface and two implementations, one for each "type" of estimate, but I can't seem to wrap my head around a common interface for both. If I create an interface without any members, then I can't access the data in a unified way and it is a bad design IMHO. I also wanted to keep the viewmodel as simple as possible. I'd get a "correct by construction" code with this approach though, since there wouldn't need to be any nullables anymore: each implementation would have a non nullable property, either a DateTime or a Range.

I also considered only using a single Range and when situation #1 happens, just use the same DateTime for both Start and End, but this would add complications on how to emit the values to the UI as I would then have to detect if it's a static or interval by comparing the values and properly format the range to either be displayed as a single date or a formatted interval.

It seems that what I need is a concept similar to Typescript's unions: basically a property that can be any of two types. No such a thing exists natively in C# of course (only dynamic would be close to that).

Best Answer

Use a date range (i.e. two dates) for all shipping estimates.

For a single date, make X and Y equal.