Найди ошибку: плохие сравнения. Часть 3

Вы обратили внимание на то, что сравнение длины строк в предыдущем примере было слишком длинным? Его можно переписать следующим образом:

 static int ByLength(string x, string y)
{
  if (x == null && y == null) return 0:
  if (x == null) return -1;
  if (y == null) return 1;
  return CompareInts(x.Length, y.Length);
}

static int CompareInts(int x, int y)
{
  // Получаем положительное значение, если x больше, отрицательное – если y больше, нуль, если они равны
  return x - y; 
}

static Comparison<T> ThenBy<T>(this Comparison<T> firstBy, Comparison<T> thenBy)
{
  return (x,y)=>
  {
    int result = firstBy(x, y);
    return result != 0 ? result : thenBy(x, y);
  }
}

Гораздо лучше! Мой метод сравнения длин строк значительно упростился, я легко могу объединять сравнения с помощью метода расширения ThenBy и я могу повторно использовать метод CompareInts в качестве вспомогательной функции в других функциях сравнения.

Где здесь ошибка?

.

.

.

.

.

.

После прошлого раза, найти разгадку будет просто. Длины строк всегда положительны и на практике никогда не превосходят нескольких миллионов; CLR не позволяет создавать огромные многогигабайтные строки. Функция CompareInts безопасна при сравнении длин строк, однако в общем случае, она небезопасна. В частности, при сравнении Int32.MinValue и Int32.MaxValue, разница равна 1. Очевидно, что минимальное целое меньше максимального целого, однако этот метод возвращает противоположный результат! Метод CompareInts должен быть таким:

 static int CompareInts(int x, int y)
{
  if (x > y) return 1;
  if (x < y) return -1;
  return 0;
}

Мораль этой истории такова: функция сравнения, которая не выполняет сравнения, скорее всего неверная.

Вычитание не является сравнением.

Оригинал статьи