How can I make my generic comparer (IComparer) handle nulls?

10.1k views Asked by At

I'm trying to write a generic object comparer for sorting, but I have noticed it does not handle the instance where one of the values it's comparing is null. When an object is null, I want it to treat it the same as the empty string. I've tried setting the null values to String.Empty but then I get an error of "Object must be of type String" when calling CompareTo() on it.

public int Compare(T x, T y)
{
    PropertyInfo propertyInfo = typeof(T).GetProperty(sortExpression);
    IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null);
    IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null);

    if (obj1 == null) obj1 = String.Empty; // This doesn't work!
    if (obj2 == null) obj2 = String.Empty; // This doesn't work!

    if (SortDirection == SortDirection.Ascending)
        return obj1.CompareTo(obj2);
    else
        return obj2.CompareTo(obj1);
}

I'm pretty stuck with this now! Any help would be appreciated.

4

There are 4 answers

1
Anthony Pegram On BEST ANSWER

You cannot treat your T as an empty string unless your T was effectively constrained to being a string. What you should do is have a plan for comparing nulls. Such as

if (obj1 == null && obj2 == null)
   return 0;
else if (obj1 == null)
   return -1;
else if (obj2 == null)
   return 1;
else 
   return obj1.CompareTo(obj2);
2
Michael K On

Since T is a generic type, you cannot assign it a String value; you can only assign it a value of type T. If you are only going to use this to compare strings, use String instead of T. Otherwise, add null checking and decide where in order null should fall.

0
Alxandr On
IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null) ?? "";
IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null) ?? "";

This basically means that obj1 will now be the value of propertyInfo.GetValue(x, null) or, if that happens to be null, obj1 will be "".

Or if the problem is that the GetValue crashes on null you could do something like:

IComparable obj1 = "";
try { obj1 = (IComparable)propertyInfo.GetValue(x, null); } catch {}
0
LukeH On
if (SortDirection == SortDirection.Ascending)
    return Comparer<T>.Default.Compare(obj1, obj2);
else
    return Comparer<T>.Default.Compare(obj2, obj1);