Often when designing my entity objects, I've used System.Object instead
of System.DateTime for dates that can be nullable. (Sidebar:
System.DateTime is a value type, so it can't be null)
In Whidbey, this is cake using
Nullable types:
public Nullable<DateTime> ShippedDate;
But for now, I've decided to use Object. Okay
choice, I've always thought. I could use the SqlDateTime which
is a refrence type implements INullable but
that seems just plain wrong. I could roll my own reference type, but that
wouldn't be good then if I exposed it at the end of a web service. There's also projects like
http://nullabletypes.sourceforge.net/
that I probably should be looking into. But, object was working for me,
so I didn't look into it too much. DataGrids and other data bound
objects
nicely handle the null values, and can also nicely format the dates, if
they're not null.
But, this bit me the other day, when I tried to sort a collection,
using the code that I posted in my
Sort and Filter Strongly-Typed Collection Classes post. What happens is that when there's a null value
ArrayList.Sort() will throw an exception.
The solution for me turned out to be adding a null
equivalent for the
actual type that I was trying to sort on.
/// <summary>
/// Gets the null equivalent.
/// </summary>
/// <param name="type">Type.</param>
/// <returns></returns>
private object GetNullEquivalent(Type type)
{
if(type == typeof(System.DateTime)) return DateTime.MinValue;
if(type == typeof(System.String)) return String.Empty;
return null;
}
But, this gets even more
complicated, because you don't know the type before hand, so you have
to do some looping to find out. Perhaps there's an
attribute that I could use to make this easier, and specify the actual
intended type, for stuff like this. Anyhow, because of this, I've had
to change the code that does the sorting from this:
/// <summary>
/// Does the sort.
/// </summary>
private void DoSort()
{
sortedList.Clear();
if (sortBy == null)
{
foreach (object obj in BaseList)
{
sortedList.Add(new ListItem(obj, obj));
}
}
else
{
foreach (object obj in BaseList)
{
sortedList.Add(new ListItem(sortBy.GetValue(obj), obj));
}
}
sortedList.Sort();
isSorted = true;
if (ListChanged != null)
{
ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, 0));
}
}
To this:
/// <summary>
/// Does the sort.
/// </summary>
private void DoSort()
{
sortedList.Clear();
if (sortBy == null)
{
foreach (object obj in BaseList)
{
sortedList.Add(new ListItem(obj, obj));
}
}
else
{
System.Type objectType = null;
// Get the type
foreach (object obj in BaseList)
{
object sorter = sortBy.GetValue(obj);
if(sorter != null && objectType == null)
{
objectType = sorter.GetType();
}
else if(sorter == null && objectType != null)
{
sorter = GetNullEquivalent(objectType);
}
else if(sorter == null && objectType == null)
{
// Loop through, and find the object's type
foreach(object obj2 in BaseList)
{
object sorter2 = sortBy.GetValue(obj2);
if(sorter2 != null)
{
objectType = sorter2.GetType();
break;
}
}
// We didn't find the object's type, set it to a string.
if(objectType == null) objectType = typeof(String);
sorter = GetNullEquivalent(objectType);
}
sortedList.Add(new ListItem(sorter, obj));
}
}
sortedList.Sort();
isSorted = true;
if (ListChanged != null)
{
ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, 0));
}
}
-Brendan