Am 25.01.2022 um 18:48 schrieb Thomas Kurz via fpc-pascal:
Consider the following code:
***
PROGRAM project1;
{$mode objfpc}
{$longstrings on} // see output below
{$modeswitch advancedrecords}
USES Variants, Generics.Collections, SysUtils;
TYPE TRecord = PACKED RECORD
FID: NativeUInt;
FKey: String;
CONSTRUCTOR Create (AID: NativeUInt; AKey: String);
END;
CONSTRUCTOR TRecord.Create (AID: NativeUInt; AKey: String);
BEGIN
FID := AID;
FKey := UpperCase (AKey);
END;
VAR
Dict: SPECIALIZE TDictionary<TRecord,Variant>;
i: SPECIALIZE TPair<TRecord,Variant>;
BEGIN
Dict := SPECIALIZE TDictionary<TRecord,Variant>.Create;
Dict.Add (TRecord.Create (1, 'test'), 1);
FOR i IN Dict DO Writeln (i.Key.FID, #9, i.Key.FKey, #9, i.Value);
// ^^^ 1 TEST 1
// -> so the entry is ok!
Writeln (Dict.ContainsKey (TRecord.Create (1, 'test')));
// ^^^ with longstrings on -> FALSE
// with longstrings off -> TRUE
Writeln (Dict.ContainsKey (TRecord.Create (1, 'TEST')));
// ^^^ always FALSE
Dict.Free;
END.
***
I'm very confused... I have no idea if I'm overseeing something or this is a
bug in generics.collections or in the compiler.
This is by design, Delphi behaves the same here. The point is that the
default comparer used for records uses binary comparison and in case of
an AnsiString that will be a pointer while with a ShortString this will
be the string contents.
The solution is to create a comparer for your custom key:
=== code begin ===
type
TRecordComparer = class(TEqualityComparer<TRecord>)
function Equals(constref Left, Right: TRecord): Boolean; override;
function GetHashCode(constref Value: TRecord): UInt32; override;
end;
function TRecordComparer.Equals(constref Left, Right: TRecord): Boolean;
begin
Result := (Left.FID = Right.FID) and (Left.FKey = Right.FKey);
end;
function TRecordComparer.GetHashCode(constref Value: TRecord): UInt32;
begin
Result := BobJenkinsHash(PChar(Value.FKey)^, Length(Value.FKey) *
SizeOf(Char), 0);
Result := BobJenkinsHash(Value.FID, SizeOf(NativeUInt), Result);
end;
begin
// and then:
Dict := specialize
TDictionary<TRecord,Variant>.Create(TRecordComparer.Create);
// and now the remaining code works
end.
=== code end ===
Regards,
Sven
_______________________________________________
fpc-pascal maillist - fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal