IComparer gotchas
Today I was fixing mystical NullReferenceException. Based on stack trace, during list sorting using standard .Sort() method with custom comparer, .Net framework passed null object to the comparer class. Initially I thought list has null element, thus causing NullReferenceException, however it turned out to be a little bit trickier.
List really didn’t have any null elements, but in some cases .Net eventually invoked .Compare() method with null values. Simplified code is shown below.
public class TestClass
{
public double Value1 { get; set; }
public double Value2 { get; set; }
}
public class TestComparer : IComparer<TestClass>
{
private readonly double _maxValue1;
private readonly double _maxValue2;
public TestComparer(double maxValue1, double maxValue2)
{
_maxValue1 = maxValue1;
_maxValue2 = maxValue2;
}
public int Compare(TestClass x, TestClass y)
{
return (1.0 * x.Value1 / _maxValue1) + (0.7 * x.Value2 / _maxValue2) > (1.0 * y.Value1 / _maxValue1) + (0.7 * y.Value2 / _maxValue2)
? 1
: -1;
}
}
In some cases either maxValue1 or maxValue2 were 0.0. For some reason I thought .Net should throw DivideByZeroException and NullReferenceException shouldn’t be related, but it is true only for integers. You can divide double values by zero, thus causing Double.Nan or Double.Infinite values. When you compare two Double.Nan values with “>” you will always get false, but if you use Double.CompareTo() method, you will get 0 (values are equal) when both values are Double.Nan.
So, original comparer implementation always returned -1 as result of comparison and in some cases it made .Net sort algorithm to pass null value. What is weird is that it fails only in IIS running in production environment. Console app on same production server, IIS running locally, IIS on another machine - everything just works.
After comparer code was fixed to avoid division by zero and use Double.CompareTo() method, those mystical NullReferenceExceptions disappeared.