2

TDictionary を使用して Delphi で関係 Car <-> Owner を実装する場合、IEqualityComparer の Equals および GetHashCode 関数をどのように実装すればよいですか? (GetHashCode は、TDictionary でのハッシュに使用される整数を返します。)

TVehicle クラスの場合、VIN (車両識別番号) があると仮定します。

VIN のハッシュコードはどのように実装すればよいですか?

更新: この例では、オブジェクト ID は「2 つのオブジェクト ポインターのメモリ位置の ID」を意味するのではなく、「プロパティの一意で不変 ("不変") な組み合わせに基づく、同じオブジェクトの 2 つのインスタンスの ID」を意味します。 .

したがって、マップ内のメモリ アドレスで車両を検索する代わりに、探している ID を持つ車両が必要です。

アプリケーションの起動時にディクショナリにロードされる車両所有者データを含むデータベースを考えてみてください。ユーザーがアプリケーション フォームに VIN を入力した場合、アプリケーションはどのようにして辞書で車両を見つけることができるのでしょうか? コードがVehicleFactory.CreateVehicleFromDatabase(Edit1.Text);このオブジェクトを使用して新しいインスタンスを作成し、辞書で検索する場合、Equals の既定の実装では、メモリ アドレスを検索するため、マップ内のエントリが見つかりません。車両を見つけるために、Equals は VIN を比較する必要があります。

したがって、カスタム IEqualityComparer を作成する必要があります。Equals の実装は簡単です。しかし、GetHashCode はどうでしょうか。文字列プロパティの場合、文字列のアドレスを単純に使用することはできません ( Delphi 文字列は不変ですか?の Berry Kelly を参照してください:「コードの 2 つの別々のセクションから同じ文字列を作成すると、同じバッキング ストアを共有しません」)。であるため、文字列プロパティの GetHashCode 関数にはカスタマイズされた実装が必要です。

Delphi で文字列をハッシュするにはどうすればよいですか? という質問も見つけました。- を含む例がありますHashValue('Hello World')

4

3 に答える 3

3

Delphi 文字列にはデフォルトのハッシュ コード実装が付属していないという誤解に陥っているようです。

これはそうではありません。文字列値をキーとしてを作成するTDictionaryと、文字列の内容に基づいてハッシュが計算されます。が文字列変数の場合Value、コードは次のようになります。

BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);

これは、文字列ハッシュに関する質問の一部に答えると思います。


他の回答へのコメント、および私が削除したものは、あなたが考えている設計上の問題に関する興味深い議論でした。TVehicle インスタンスと VIN の間に多対 1 の関係を許可することが正しい解決策であるというあなたの考えには、まだ懐疑的です。

VIN が同じでデータが異なる複数の TVehicle インスタンスを使用してはならないことを確認しました。これを実現する最善の方法は、TVehicle インスタンスと VIN の間に 1 対 1 の関係を確保することです。

この 1 対 1 の関係は、非常に簡単に実現できます。TVehicle インスタンスのインスタンス化をファクトリ クラス専用の関数にする必要があります。このファクトリ クラスは、既存の車両インスタンスを含むディクショナリを保持しますTDictionary<string,TVehicle>。車両を手に入れる必要がある場合は、工場に依頼してください。辞書にある既存のものを返すか、新しいものを合成します。

この効果を実現するには他にも多くの方法があることは間違いありませんが、VIN ごとに車両インスタンスが 1 つだけになるようなアプローチを検討することを強くお勧めします。

于 2011-03-15T21:09:15.980 に答える
3

可能であれば、これに KISS の原則を適用します。実際のキーが車両自体ではなく ID である場合は、 ? のTDictionary<string, TPerson>代わりにa を使用してみませんTDictionary<TVehicle, TPerson>か? そうすれば、カスタムの比較子について心配する必要はありません。

于 2011-03-15T18:30:20.457 に答える
3

デザインの臭いなどについてアドバイスを受けましたので、オブジェクト キー付き辞書を作成し、キーのメモリ アドレスとは異なるものに基づいて比較することは有効であるため、ご質問にお答えします。

TDictionary の作成時に新しい比較子を作成できます。

例えば:

type
  TVehicleOwner = class (TDictionary<TVehicle, TOwner>)
  end;

//other code here

procedure TForm2.Button1Click(Sender: TObject);
var
  VehOwner: TVehOwner;
begin
  VehOwner := TVehOwner.Create(TEqualityComparer<TVehicle>.Construct(
    //comparer
    function(const Left, Right: TVehicle): Boolean
    begin
      { Make a case insensitive comparison }
      Result := CompareText(Left.FID, Right.FID) = 0;
    end,
    //hasher
    function(const Value: TVehicle): Integer
    begin
      { Generate a hash code. }
      Result := TheHashAlgorythmOfYourChoice(Value.FID);
    end)
  );

  //more code here

これは、同じオブジェクトを表す 2 つのインスタンスがある場合、コードに欠陥があると思います。ID が「ABC」の TVehicle がメモリにある場合、私の場合、これがその車両の唯一のインスタンスである必要があり、すべてのコードに対してこの同じインスタンスを取得する方法を提供する必要があります。そうすれば、カスタムの比較子を記述せずに Dictionary クラスを使用できますが、さらに重要なことは、常に同じオブジェクトを使用していることを知っており、アプリケーションの状態は、コード、UI、またはその他のインターフェイスのすべてのものと一貫しているように見えることです。 .

于 2011-03-15T20:51:34.197 に答える