Вопрос

Если я внедряю автомобиль отношений <--> в Delphi, используя Tdictionary, как мне реализовать функцию equals и gethashcode в iequalityComparer? (Gethashcode возвращает целое число, которое используется для хэширования в Tdictionary.)

Для класса Tvehicle предположим, что он имеет VIN (номер идентификации транспортного средства).

Как мне реализовать хэшкод для VIN?

Обновление: в этом примере идентичность объекта не означает «идентичность местоположений памяти двух указателей объекта», а «идентичность двух экземпляров одного и того же объекта, основанного на уникальном и неизбежном (»неизменный«) Комбинация его свойств».

Поэтому вместо того, чтобы искать автомобиль по адресу памяти на карте, мне нужен автомобиль, у которого есть идентификатор, который я ищу.

Подумайте о базе данных, которая содержит данные владельца транспортного средства, загруженную в словарь при запуске приложения. Теперь, если пользователь входит в VIN в форму заявки, как приложение может найти автомобиль в словаре? Если код создает новый экземпляр с использованием VehicleFactory.CreateVehicleFromDatabase(Edit1.Text); и поиск этого объекта в словаре, реализация равных по умолчанию не найдет никаких записей на карте, потому что он ищет адрес памяти. Чтобы найти транспортное средство, Equals необходимо сравнить VIN.

Поэтому я должен создать пользовательский iequalityComperer. Реализация равна тривиальна. Но как насчет Gethashcode? Для свойства строки я не могу просто использовать адрес строки (см. Берри Келли в Неужели Delphi Strings неизменны? : «Если вы создаете одну и ту же строку из двух отдельных разделов кода, они не будут делиться одним и тем же хранилищем поддержки»), поэтому функция GethashCode для свойства строки нуждается в настраиваемой реализации.

Я также обнаружил, что нашел вопрос Как мне хэт -строку с Delphi? - Есть пример, который содержит HashValue('Hello World')

Это было полезно?

Решение

Похоже, вы были введены в заблуждение в убеждении, что строки Delphi не поставляются с реализацией хэш -кода по умолчанию.

Это не тот случай. Когда вы создаете TDictionary С помощью строкового значения в качестве ключа хэш рассчитывается на основе содержимого строки. Если Value это строковая переменная, тогда код выглядит так:

BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);

Я думаю, что это отвечает на часть вашего вопроса, касающегося строкового хеширования.


Комментарии к другим ответам, и те, которые я удалил, были интересным обсуждением проблемы дизайна, которую вы рассматриваете. Я все еще скептически отношусь к вашей убеждению, что правильное решение состоит в том, чтобы позволить взаимосвязь между экземплярами Twehicle и VIN.

Вы подтвердили, что у вас не должно быть нескольких экземпляров Tvehicle с тем же VIN, но разные данные. Мне кажется, что лучший способ достичь этого-убедиться, что у вас есть отношения один к одному между экземплярами и ВИН.

Эти отношения один к одному довольно легко достичь. Вам нужно сделать экземпляр экземпляров TVEHICLE функцией частной для фабричного класса. Этот фабричный класс имеет словарь, содержащий существующие экземпляры транспортных средств, TDictionary<string,TVehicle>. Анкет Если вам нужно получить автомобиль, вы спросите за фабрикой. Он возвращает либо существующий, который был расположен в его словаре, либо синтезирует новый.

Нет сомнений в том, что есть ряд других способов достичь этого эффекта, но я настоятельно призываю вас рассмотреть подход, который приводит только к одному экземпляру автомобиля на VIN.

Другие советы

Я бы бросил принцип поцелуя, если это возможно. Если ваш фактический ключ - это идентификатор, а не самого транспортного средства, то почему бы не использовать TDictionary<string, TPerson> вместо TDictionary<TVehicle, TPerson>? Тогда вам не придется беспокоиться о пользовательских сравнениях.

Будучи консультированы о запахе в вашем дизайне и других вещах, я отвечу на ваш вопрос, так как он действителен, чтобы создать словарь с ключом объекта и сравнить его на основе чего -либо, отличного от адреса памяти ключа:

Вы можете создать новый сравнитель во время создания TDICTIONARY.

Например:

type
  TVehicleOwner = class (TDictionary<TVehicle, TOwner>)
  end;

//other code here

procedure TForm2.Button1Click(Sender: TObject);
var
  VehOwner: TVehOwner;
begin
  VehOwner := TVehOwner.Create(TEqualityComparer<TVehicle>.Construct(
    //comparer
    function(const Left, Right: TVehicle): Boolean
    begin
      { Make a case insensitive comparison }
      Result := CompareText(Left.FID, Right.FID) = 0;
    end,
    //hasher
    function(const Value: TVehicle): Integer
    begin
      { Generate a hash code. }
      Result := TheHashAlgorythmOfYourChoice(Value.FID);
    end)
  );

  //more code here

При этом, я думаю, что это недостаток в вашем коде, если у вас есть два экземпляра, представляющих один и тот же объект. Если у вас есть Tvehicle с идентификатором «ABC» в памяти, что для меня, это должен быть единственный экземпляр этого автомобиля, и вы должны предоставить какой -то способ получить этот же экземпляр для всего вашего кода. Таким образом, вы можете использовать класс словаря без написания пользовательского сравнения, но что более важно, вы знаете, что работаете все время с одним и тем же объектом, и состояние вашего приложения будет и казаться согласованным для чего -либо в коде, пользовательском интерфейсе или других интерфейсах Анкет

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top