IEqualityComparer и строка

Почему IEqualityComparer не работает со строками?

type
    TRec = record
        s: string;
    end;

var
    rec1, rec2: TRec;
    comparer: IEqualityComparer<TRec>;
    res: boolean;
begin
    rec1.s := 'a';
    rec2.s := 'a';

    comparer := TEqualityComparer<TRec>.default;
    res := comparer.equals(rec1, rec2);
    showMessage(boolToStr(res));

Если запись TRec содержит числовое значение или строку определенной длины, то IEqualityComparer работает корректно. Как заставить этот код работать?


person nup    schedule 12.10.2018    source источник
comment
Он работает для строк, но вы не передаете строку. Вы проходите запись. А для записей компаратор просто выполняет бинарное сравнение. Вам нужно написать свой собственный компаратор.   -  person David Heffernan    schedule 12.10.2018
comment
См. TEqualityComparer‹T› может дать сбой для записей из-за выравнивания. Напишите свой компаратор.   -  person LU RD    schedule 12.10.2018
comment
Вместо написания собственного компаратора просто используйте TEqualityComparer.Construct.   -  person Rudy Velthuis    schedule 12.10.2018
comment
@Rudy Velthuis Является ли компаратор записи неточным термином для построения компаратора, как я сделал?   -  person MBo    schedule 12.10.2018
comment
На мой взгляд, написание компаратора означало бы написание нового класса, наследование и т. д. Я бы не стал использовать Construct с анонметом, пишущим компаратор, но я согласен, что это зависит от интерпретации.   -  person Rudy Velthuis    schedule 12.10.2018
comment
@rudy Construct — идиоматический способ написать компаратор   -  person David Heffernan    schedule 12.10.2018
comment
@David: Просто я бы не назвал это написанием компаратора. Просто создание экземпляра.   -  person Rudy Velthuis    schedule 12.10.2018


Ответы (1)


Изменить:

Как заметил в комментариях Руди Вельтус, в свежих версиях Delphi результат верен для одинаковых строк constant, потому что они используют одну и ту же память и имеют один и тот же адрес (поэтому мое прежнее предположение о лучшем RTTI неверно).

Для сложных типов компаратор по умолчанию по-прежнему выбирает низкоуровневый компаратор, который сравнивает необработанные байты обеих записей — разные адреса для одинаковых тел строк во втором случае моего примера.

Таким надежным подходом является создание собственного компаратора для работы со сложными типами - см. третий пример ниже.

type
    TRecS = record
        s: string;
    end;
 var
    rec1, rec2: TRecS;
    comparerS: IEqualityComparer<TRecS>;
    cmp: IEqualityComparer<TRecS>;
    res: boolean;
begin
    rec1.s := 'const';
    rec2.s := 'const';
    comparerS := TEqualityComparer<TRecS>.default;
    res := comparerS.equals(rec1, rec2);
    Memo1.Lines.Add(boolToStr(res));

    rec1.s := IntToStr(88);
    rec2.s := IntToStr(88);
    res := comparerS.equals(rec1, rec2);
    Memo1.Lines.Add(boolToStr(res));

    cmp := TEqualityComparer<TRecS>.Construct(
                      function(const Left, Right: TRecS): Boolean
                      begin
                          Result := Left.S = Right.S
                      end,
                      nil);
   res := cmp.equals(rec1, rec2);
   Memo1.Lines.Add(boolToStr(res));

    -1  //denotes true
    0  
    -1
person MBo    schedule 12.10.2018
comment
Я использую Delphi XE7, и как мне решить эту проблему здесь? - person nup; 12.10.2018
comment
Сделать собственный компаратор. Я привел простейший пример. - person MBo; 12.10.2018
comment
@downvoter - если вы считаете, что мой ответ неверен, сообщите нам об ошибках - person MBo; 12.10.2018
comment
Насколько я знаю, в Токио вы все еще получаете двоичный компаратор. Но если обеим строкам присваивается константа, они одинаковы (т. е. указывают на одну и ту же константу), поэтому даже при двоичном сравнении они рассматриваются как равные. Но если вы это сделаете: rec1.s := IntToStr(1234); rec2 := IntToStr(1234); строки имеют разные адреса, но все равно должны сравниваться как равные. Мой вывод в Токио показывает, что они не равны, потому что есть две разные строки (разные адреса, но одинаковая полезная нагрузка). Это показывает, что они все еще выполняют бинарное сравнение, если вы не используете Construct... - person Rudy Velthuis; 12.10.2018
comment
... Я надеялся, что они будут использовать информацию о типе для создания интерфейса, который будет проверять каждое поле по отдельности. Нет, все еще бинарное сравнение записи. - person Rudy Velthuis; 12.10.2018