Delphi Prism: как переопределить методы GetHashCode и Equals для правильной работы IndexOf?

Я не уверен, правильно ли я это делаю. У меня есть список объектов в списке, и мне нужно использовать IndexOf, чтобы получить индекс объекта в списке.

if AlarmListBox.items.indexOf(alrm.Tagname) = -1 then

alrm - это объект класса TAlarm.

На основе вопроса StackOverflow C # (Как Могу ли я получить индекс элемента в ListBox?), я пытаюсь переопределить метод GetHashCode и Equals, но он все равно не работает.

Метод переопределения:

TAlarm = class(System.Object)
  TagName:string;
  private
  protected
  public
  method Equals(obj:System.Object):Boolean; override;
  method GetHashCode:Int32; Override;
end;

method TAlarm.Equals(obj: system.Object):Boolean;
begin
    result := TAlarm(obj).Tagname.Equals(self.Tagname);
end;

method TAlarm.GetHashCode:Int32;
begin
    result := self.GetHashCode;
end;

Вот как я заполняю AlarmListBox:

AlmGrp:= new TAlarmGroup;
AlarmListBox.items.Add(AlmGrp);

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

Правильно ли я реализую эти переопределения? Если нет, как мне их переопределить?

Будем признательны за образец кода, подсказки или подсказки. Спасибо,

ОБНОВЛЕНИЕ: Дэвиду Хеффернану и другим, кто прокомментировал или ответил, я думаю, что проблема может заключаться в том, что я передаю два разных объекта, как говорится в последнем комментарии Роба. Я заполняю Listbox (UI) с помощью TAlarmGroup, но передаю TAlarm в IndexOf, хотя оба они являются идентичными классами. Это наверное моя проблема. Что я действительно пытаюсь сделать, так это заполнить Listbox объектами TAlarmGroup и через listbox.indexof, передав строку (Tagname), которую я ищу для местоположения объекта. Вот как это сделано в Delphi XE, он отлично работает. Приведенный выше код не является фактическим кодом. Как только я устраню путаницу в своем коде, он, вероятно, будет работать без переопределения методов GetHashcode и Equals.

ОБНОВЛЕНИЕ: Думаю, я кое-что здесь наткнулся. В Delphi XE или более ранней версии ListBox (UI) предоставляет метод под названием AddObject. Его параметры - это строка и объект соответственно. Итак, когда я заполнил объекты в список, я также предоставил строку, которая будет им соответствовать. Когда я искал, я вводил строку или имя группы сигналов тревоги. IndexOf произвел поиск в этой строке по строке, которую он имел для каждого предоставленного мной объекта, а не по полю объекта (TagName). В Delphi Prism у listbox нет метода, аналогичного методу AddObject, а есть только Add, который принимает объект только в качестве параметра.


person ThN    schedule 15.09.2011    source источник
comment
indexOf должен работать из коробки. Не так ли?   -  person David Heffernan    schedule 16.09.2011
comment
Интересно, как работает TAlarm.GetHashCode. Я предполагаю, что это вызывает переполнение стека. Если этого не произойдет, я предполагаю, что он никогда не будет вызван. Мне действительно интересно, почему он вообще был перезаписан. @David: если TagName является критерием равенства, показанный код должен быть в порядке (хотя и немного неуклюже).   -  person Rudy Velthuis    schedule 16.09.2011
comment
@ David, я так думал изначально, но всегда возвращал -1, даже если объект уже был добавлен в список. Итак, я решил, что мне нужно переопределить этот метод после прочтения чьего-то другого вопроса о stackoverflow.   -  person ThN    schedule 16.09.2011
comment
@ Руди, ты прав. На самом деле, я просто отлаживал их построчно, и они никогда не вызывались.   -  person ThN    schedule 16.09.2011
comment
Быстрый тест показывает, что Items.IndexOf работает и здесь. Что произойдет, если вы не переопределите Equals и GetHashCode, а просто используете IndexOf? (Кроме того, в качестве отступления: вы можете изменить свой код Equals на просто Result := TAlarm(obj).TagName.Equals(self.TagName);. Назначение result := false; и if..then в тесте можно исключить.   -  person Ken White    schedule 16.09.2011
comment
@ Ken White, насколько я понимаю, IndexOf должен работать из коробки, как сказал Дэвид. Вот как я использовал этот метод в своем коде. Когда это не сработало, что бы я ни делал, я начал искать ответ и наткнулся на вопрос о Stackoverflow. Итак, я решил посмотреть, поможет ли это, но пока нет. Фактически, IndexOf все время работает с одним и тем же значением, возвращающим -1.   -  person ThN    schedule 16.09.2011
comment
Вы можете показать, как вы заполняете ListBox.Items?   -  person Ken White    schedule 16.09.2011
comment
Название AlarmListBox предполагает, что это элемент управления пользовательского интерфейса ListBox. Такие элементы управления содержат строки, а не TAlarm объекты, хотя вы добавляете к нему TAlarmGroup, который также не TAlarm, так зачем вам вообще нужны TAlarm методы? ? Вы демонстрируете поиск элемента, соответствующего alrm.name, но ваш TAlarm класс не имеет name члена. Может ли причина того, что вы не находите то, что ищете, просто в том, что того, что вы ищете, действительно нет в списке?   -  person Rob Kennedy    schedule 16.09.2011
comment
Пожалуйста, проясните свой вопрос. Покажите нам полный код. Это TAlarm или TAlarmGroup? Что такое DisplayMember для вашего списка? Вы реализуете ToString? Почему вы вызываете IndexOf, передавая строку? Вы обязательно должны передать объект?   -  person David Heffernan    schedule 16.09.2011
comment
@rob WinForms ListBox содержит объекты. Свойство DisplayMember определяет член, который будет использоваться для получения отображаемого имени. Если не определено, используется ToString. Немного отличается от VCL.   -  person David Heffernan    schedule 16.09.2011


Ответы (1)


Вот пример того, как вы делаете то, что хотите, с предоставленным вами базовым классом TAlarm. Я также предоставил реализации перегруженных Equals и GetHashCode, которые, похоже, работают. (Опять же, я не разработчик Prism / .NET; просто пытаюсь здесь помочь.)

// In AlarmClass.pas
type
  TAlarm = class(System.Object)
    TagName:string;
  private
  protected
  public
    constructor;
    method Equals(obj:System.Object): Boolean; override;
    method GetHashCode:Int32; Override;
    method ToString(): String; override;
end;

implementation

method TAlarm.GetHashCode: Int32;
begin
  if Self = nil then
    Result := inherited 
  else
    Result := Self.TagName.GetHashCode;
end;

constructor TAlarm;
begin
  inherited;
end;

method TAlarm.Equals(obj: System.Object): Boolean;
begin
  if  (obj = nil) or (GetType() <> obj.GetType()) then
    Exit(False);
  Result := TAlarm(obj).TagName.Equals(Self.TagName);
end;

method TAlarm.ToString(): String;
begin
  Result := Self.TagName;
end;

// In MainForm.pas
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
var
  Idx: Integer;
begin
  Idx := ComboBox1.SelectedIndex;
  if Idx <> -1 then
    ListBox1.SelectedIndex := ListBox1.Items.IndexOf(ComboBox1.Items[Idx]);
end;

method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs);
var
  i, j: Integer;
  Alarm: TAlarm;
  aList: Array[0..4] of Object;
  aFind: Array[0..1] of Object;
begin
  j := 0;
  for i := 0 to 4 do
  begin
    Alarm := new TAlarm;
    Alarm.TagName := String.Format('Alarm{0}', i);
    aList[i] := Alarm;
    // Place items 1 & 3 in another array of searchable items -
    // just for fun. Not suggesting implementing your app this way
    // by any means.
    if (i mod 2) > 0 then
    begin
      aFind[j] := Alarm;
      Inc(j);
    end;
  end;
  ListBox1.Items.AddRange(aList);
  ComboBox1.Items.AddRange(aFind);
end;

Вот как это выглядит с элементом, выбранным в ComboBox после нажатия Button:

Снимок экрана объекта, найденного в ListBox

person Ken White    schedule 15.09.2011
comment
Вы просто добавляете строки в список. Зачем возиться с TAlarm? Здесь тебе это не нужно. - person David Heffernan; 16.09.2011
comment
Элементы WinForms ListBox часто являются объектами, а не строками. Я считаю, что именно это и намерены делать диджитал. - person David Heffernan; 16.09.2011
comment
@ Дэвид, мне нужно добавить объекты, а затем выполнить поиск в списке, передав имя объекта в indexof. Позвольте мне попробовать код Кена. - person ThN; 16.09.2011
comment
Я хотел сказать, что Equals действительно вызывается, если вы правильно его переопределите и используете. Это не имело ничего общего с добавлением объектов в ListBox; прочтите первое предложение. Это должно было показать, что Роб, вероятно, был прав в своем комментарии, что то, что добавлялось к ListBox, не было объектами Alarm, и что, вероятно, это было причиной того, что Equals не был вызван. (Кроме того, после прочтения первого предложения, пожалуйста, прочтите последнее (под изображением).) :) Но, @David, пожалуйста, не стесняйтесь публиковать свой ответ, и я с радостью удалю свой. :) - person Ken White; 16.09.2011
comment
@Ken digital объяснил, что он хочет делать, и это согласуется с моей интерпретацией (на этот раз). - person David Heffernan; 16.09.2011
comment
Как бы то ни было, если вы добавляете объекты в список (что вам необходимо), вы не можете передавать строки в IndexOf, поскольку я читал документацию WinForms. - person David Heffernan; 16.09.2011
comment
@DavidHeffernan - Переписано. Теперь лучше? :) - person Ken White; 16.09.2011
comment
да. По-прежнему похоже, что вы передаете объект IndexOf(). Я имею в виду, я думаю, что это то, что вам нужно сделать, но, конечно, @digital ожидает другого. Здесь я исчерпал свои знания. Я бы просто использовал наивный список, чтобы перебирать список и проверять имена. Но для этого должен быть собственный .net / WinForms способ. - person David Heffernan; 16.09.2011
comment
В этом был смысл; Я передаю объект, но нахожу его в ListBox по имени. (Ранее вы указали, что я не могу передавать строки в IndexOf, потому что я добавил Objects, поэтому я нашел его, передав вместо этого Object и используя переопределенное Equals для сравнения по имени. - person Ken White; 16.09.2011
comment
@ Кен, спасибо за отличный пример. Я пришел к выводу. У меня такие ужасные времена со списком, который должен быть очень простым в использовании, потому что на самом деле я имею дело с двумя разными классами, даже если они идентичны. Я загружаю объекты класса TAlarmGroup и ищу объекты передачи индекса класса TAlarm. Это основная проблема. Мне нравится твой пример. Судя по всему, мне все еще может понадобиться реализовать переопределения. - person ThN; 16.09.2011
comment
@Ken Конечно, ComboBox1.Items[Idx] - это TAlarm, а не string. Или я неправильно прочитал? FWIW, вам не нужно объявлять экземпляры сигналов тревоги как Object, ваши массивы должны быть TAlarm[] или каким-либо другим эквивалентом Prism. - person David Heffernan; 16.09.2011
comment
@ Дэвид: Спасибо. Я буду иметь это в виду для использования в будущем. Касательно: ComboBox.Items и TAlarm - вы, кажется, придираетесь к тому, что я реализую то, что вы специально сказали, что необходимо сделать, поэтому я просто уйду, пока я впереди. Вопрос был конкретно в том, как реализовать Equals и GetHashCode, чтобы они работали с IndexOf, и я ответил на этот вопрос. В следующий раз, когда появится вопрос о Prism, опубликуйте свою попытку ответа, и я не буду публиковать его. :) - person Ken White; 16.09.2011
comment
@digitalanalog: Надеюсь, хоть что-то из этого будет полезно. Если нет, то для меня это был интересный опыт обучения; как я уже сказал, у меня есть Prism как часть RAD Studio, но до этого вопроса мне не приходилось использовать ее. Спасибо. :) - person Ken White; 16.09.2011
comment
Призма мне сложно задавать вопросы, так как у меня ее нет. Но я знаю кое-что о winforms. Ваши две попытки были строкой / строкой и объектом / объектом, но не объектом / строкой. Я недостаточно знаю семантику Equals, чтобы быть авторитетным. - person David Heffernan; 16.09.2011
comment
@ Дэвид, меня впечатлило то, что у кого-то нет Prism, но он все еще отвечает на множество вопросов о Prism. Это показывает, насколько вы хороший программист. Еще раз спасибо. - person ThN; 17.09.2011
comment
@digital Думаю, у меня должна быть латунная шея !! Забавно то, что на прошлой неделе я помог вам использовать WinForms, а не чистую Prism, но я также никогда не использую WinForms! - person David Heffernan; 18.09.2011