レコードのデフォルトの比較子は、パディングなしの純粋な値型のレコードに対してのみ機能します。それに頼ることは、一般的には良い考えではありません。正確なハッシングと等価性の比較を必要とするレコードについては、あなた自身の比較者を書く必要があります。
すべてのレコードをDefault()
で初期化することもできますが、この方法は面倒でエラーが発生しやすくなります。レコードの初期化を忘れることは容易であり、そのような省略をトレースすることは困難です。それは起こる。アプローチは
これなど、カスタムの比較子も参照型を扱うことができますがパディングに関連するエラーを排除することでも唯一の効果的である、例えば、問題の実用的なソリューションを示しています
program Project1;
uses
SysUtils, Windows, StrUtils, Generics.Collections, Generics.Defaults,
System.Hash;
type
TPairComparer<TKey, TValue> = class(TEqualityComparer<TPair<TKey, TValue>>)
public
function Equals(const Left, Right: TPair<TKey, TValue>): Boolean; override;
function GetHashCode(const Value: TPair<TKey, TValue>): Integer; override;
end;
TInt64IntDict<TValue> = class(TDictionary<TPair<Int64, Integer>, TValue>)
public constructor Create;
end;
function TPairComparer<TKey, TValue>.Equals(const Left: TPair<TKey, TValue>;
const Right: TPair<TKey, TValue>) : boolean;
begin
result := TEqualityComparer<TKey>.Default.Equals(Left.Key, Right.Key) and
TEqualityComparer<TValue>.Default.Equals(Left.Value, Right.Value);
end;
{$IFOPT Q+}
{$DEFINE OVERFLOW_ON}
{$Q-}
{$ELSE}
{$UNDEF OVERFLOW_ON}
{$ENDIF}
function TPairComparer<TKey, TValue>.GetHashCode(const Value: TPair<TKey, TValue>) : integer;
begin
result := THashBobJenkins.GetHashValue(Value.Key, SizeOf(Value.Key), 23 * 31);
result := THashBobJenkins.GetHashValue(Value.Value, SizeOf(Value.Value), result * 31);
end;
{$IFDEF OVERFLOW_ON}
{$Q+}
{$UNDEF OVERFLOW_ON}
{$ENDIF}
constructor TInt64IntDict<TValue>.Create;
begin
inherited Create(0, TPairComparer<Int64, Integer>.Create);
end;
var
i1, i2: TPair<Int64, Integer>;
LI64c : TPairComparer<Int64, Integer>;
LDict : TInt64IntDict<double>;
begin
FillMemory(@i1, SizeOf(i1), $00);
FillMemory(@i2, SizeOf(i1), $01);
i1.Key := 2;
i1.Value := -1;
i2.Key := i1.Key;
i2.Value := i1.Value;
WriteLn(Format('i1 key = %d, i1 value = %d', [i1.Key, i1.Value]));
WriteLn(Format('i2 key = %d, i2 value = %d', [i2.Key, i2.Value]));
WriteLn; WriteLn('Using Default comparer');
if TEqualityComparer<TPair<Int64, Integer>>.Default.Equals(i1, i2) then
WriteLn('i1 equals i2') else WriteLn('i1 not equals i2');
if TEqualityComparer<TPair<Int64, Integer>>.Default.GetHashCode(i1) =
TEqualityComparer<TPair<Int64, Integer>>.Default.GetHashCode(i2) then
WriteLn('i1, i2 - hashes match') else WriteLn('i1, i2 - hashes do not match');
WriteLn; WriteLn('Using custom comparer');
LI64c := TPairComparer<Int64, Integer>.Create;
if LI64c.Equals(i1, i2) then
WriteLn('i1 equals i2') else WriteLn('i1 not equals i2');
if LI64c.GetHashCode(i1) = LI64c.GetHashCode(i2) then
WriteLn('i1, i2 - hashes match') else WriteLn('i1, i2 - hashes do not match');
WriteLn;
LDict := TInt64IntDict<double>.Create;
LDict.Add(i1, 1.23);
if LDict.ContainsKey(i2) then
WriteLn('Dictionary already contains key') else
WriteLn('Dictionary does not contain key');
ReadLn;
end.
これは、生産します出力
I1キー= 2、I1値= -1
I2キー= 2、I2値= -1既定の比較を使用
I1ないi2の
I1、I2と等しい - ハッシュが
辞書が既にキー
が含ま一致 - ハッシュが
カスタム比較子を使用して
i1がi2の
I1、I2に等しいと一致していませんがつまり、デービッドの答えが示すように、委任された比較演算子を使用することでオーバーヘッドが少なくなり、実際に優先されるはずです。
個別の比較対象を作成する必要があります。私の見解は、レコードのデフォルト比較が存在する欠陥IDです。 –
そのため、レコードを初期化するには、デフォルト()を使用する必要があります。 AFAIKは、パディングメモリをゼロにする処理も行うコードを生成します。 –
@Stefan:あなたが正しいかもしれませんが、これはレコードを使用するのが本当に不便ですし、TDictionaryがどう扱うのかは私のコントロールの範囲を超えています。 –