Early Wednesday morning I arrived at the Kansas City Convention Center for KCDC, one of my favorite conferences. I chose the right door to enter the facility because immediately inside was my good friend Heather Downing.
Heather was hard at work putting the finishing touches on part of VML‘s sponsor booth exhibit – an Alexa skill to retrieve session and speaker information from the KCDC conference API. She already had the device responding to utterances such as “Alexa, ask KCDC when {speaker name} is speaking” and “Alexa, ask KCDC which sessions are about {topic}” but the final task, “Alexa, ask KCDC which sessions are next” was misbehaving by not returning the list of sessions at 8:30 on Thursday morning. She was clearly getting frustrated after fighting with it for some time and asked for a rubber duck and a second set of eyes.
The conference’s API returned sessions according to which time slot they’re in rather than the exact time so Heather had a list of objects that mapped the slots to the time along with some additional information unrelated to this issue. The method for selecting the time slot looked basically like this (simplified for brevity):
public static KeyValuePair<string, DateTime> GetNextSlot(DateTime start) => SharedValues .SlotInformation .Where(sv => sv.Value >= start) .OrderBy(sv => sv.Value) .FirstOrDefault();
The SharedValues
type exposes readonly
fields that describe the various slots. Since this particular skill was intended for demonstration purposes and the slots weren’t changing, all of the values were hard-coded.
The call to GetNextSlot
was equally straightforward:
// searchDate was UtcNow converted to Central time var nextSlot = GetNextSlot(searchDate);
Since the next defined slot occurred at 8:30 AM (Central) it was bizarre that GetNextSlot
was returning null. At first we thought that there was something was off with the conversion from UTC to Central time but even that should have returned something since the offset is only six hours and we were testing on Wednesday, not Thursday!
We mucked around with several different permutations before landing on the culprit – the initalization order in SharedValues
!
As she had been building out the application Heather realized she needed the slot times in multiple places so she extracted the values to separate readonly fields so they could be referenced independently. The resulting code looked like this:
public static class SharedValues { public static readonly List SlotInformation = new List { new SlotDescription ("Thursday, 8:30 AM", Slot1), new SlotDescription ("Thursday, 9:45 AM", Slot2), new SlotDescription ("Thursday, 11:00 AM", Slot3), new SlotDescription ("Thursday, 1:00 PM", Slot4), new SlotDescription ("Thursday, 2:45 PM", Slot5), new SlotDescription ("Thursday, 3:30 PM", Slot6) }; public static readonly DateTime Slot1 = new DateTime(2017, 8, 3, 8, 30, 0); public static readonly DateTime Slot2 = new DateTime(2017, 8, 3, 9, 45, 0); public static readonly DateTime Slot3 = new DateTime(2017, 8, 3, 11, 0, 0); public static readonly DateTime Slot4 = new DateTime(2017, 8, 3, 13, 0, 0); public static readonly DateTime Slot5 = new DateTime(2017, 8, 3, 14, 15, 0); public static readonly DateTime Slot6 = new DateTime(2017, 8, 3, 15, 30, 0); }
At first everything looks fine but sure enough, the fields are initialized in the order the compiler encounters them so SlotInformation
is initialized first followed by each of the Slot
values. Since each Slot
value is DateTime
and DateTime
is a value type, the default value of 01/01/0001 00:00:00
is used when the SlotDescription
instances are created!
As soon as we swapped around the initialization order (putting the Slot
values before SlotInformation
) everything worked exactly as we expected and we were finally able to ask “Alexa, ask KCDC which sessions are next.”
This just goes to show how experienced developers get caught up in a problem, they can spend way too much time digging into the complex while totally overlooking the obvious. If you’d like to observe this behavior in action check out the code sample over on repl.it.