Я интегрирую класс географических координат из CodePlex в мою личную библиотеку «инструментов». Этот класс использует float
поля для хранения широты и долготы.
Поскольку класс GeoCoordinate
реализует IEquatable<GeoCoordinate>
, я обычно писал метод Equals
примерно так:
public bool Equals(GeoCoordinate other)
{
if (other == null) {
return false;
}
return this.latitude == other.latitude && this.longitude == other.longitude;
}
В этот момент я остановился и подумал, что я сравниваю переменные с плавающей запятой на равенство, которое обычно является нет-нет. Мой мыслительный процесс тогда шел примерно так:
Я могу только представить, что установил свойства Latitude
и Longitude
один раз, что означает, что не будет накоплено никаких ошибок, чтобы испортить мои сравнения.
С другой стороны, можно (хотя и бессмысленно) написать
var geo1 = new GeoCoordinate(1.2, 1.2);
var geo2 = new GeoCoordinate(1.2, 1.2);
// geo1.Equals(geo2) will definitely be true, BUT:
geo2.Latitude *= 10;
geo2.Latitude /= 10;
// I would think that now all bets are off
Конечно, это не то, что я могу себе представить, но если общедоступный интерфейс класса позволяет это, тогда Equals
сможет справиться с этим.
Сравнение на равенство с использованием теста difference < epsilon
решило бы проблему сравнения двух экземпляров, но создало бы больше проблем:
- Как сделать равенство транзитивным? Звучит невозможно.
Как получить такой же хеш-код для всех значений, которые будут сравниваться равными?
Допустим, что epsilon = 0.11
(случайный пример). Отсюда следует, что для GeoCoordinate { 1, 1 }
потребуется тот же хеш-код, что и для GeoCoordinate { 1.1, 1.1 }
. Но последнему потребуется тот же хэш-код, что и GeoCoordinate { 1.2, 1.2 }
. Вы можете видеть, куда это идет: все экземпляры должны иметь одинаковый хэш-код .
Решением всего этого было бы сделать GeoCoordinate
неизменным классом. Это также решило бы проблему GetHashCode
: она основана на широте и долготе (что еще), и если они изменчивы, то использование GeoCoordinate
в качестве ключа в словаре вызывает проблемы. Однако для того, чтобы сделать класс неизменным, есть свои недостатки:
- Вы не можете создавать и настраивать экземпляры класса (парадигма WPF), что может быть проблемой в некоторых случаях
- Сериализация, вероятно, также станет проблемой из-за потери конструктора без параметров (я не эксперт по сериализации .NET, так что это так много деталей, как я вижу здесь)
Какой подход вы бы предложили? Легко заставить класс соответствовать требованиям, которые у меня есть сейчас (просто сделать его неизменным), но есть ли лучший способ?
Редактировать : я добавил элемент 3 в список выше, переместив предыдущий элемент 3 в положение 4.
Решение
Я собираюсь дать больше времени для обратной связи, но сейчас я иду с неизменным подходом. struct
(потому что это то, что есть сейчас) с соответствующими членами можно увидеть здесь ; комментарии и предложения приветствуются.