Search code examples
delphidelphi-xe7

IEqualityComparer and string


Why is IEqualityComparer not working with strings?

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));

If the TRec entry contains a numeric value or a string of a certain length, then IEqualityComparer works correctly. How to make this code works?


Solution

  • Edit:

    As Rudy Velthuis noticed in comments, in fresh Delphi versions result is true for equal constant strings because they share the same memory and have the same address (so my former supposition about better RTTI is wrong).

    For complex types equality default comparer still chooses low-level comparer that compares raw bytes of both records - different adresses for similar string bodies in the second case of my example.

    So reliable approach is constructing own comparer to work with complex types - see the third example below.

    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