Delphi Equals和GetHashCode用于TDictionary<;TVehicle,TPerson>;

Delphi Equals和GetHashCode用于TDictionary<;TVehicle,TPerson>;,delphi,generics,hashmap,tdictionary,Delphi,Generics,Hashmap,Tdictionary,如果我使用TDictionary在Delphi中实现车主关系,我应该如何实现IEqualityComparer的Equals和GetHashCode函数?(GetHashCode返回一个整数,该整数在TDictionary中用于哈希。) 对于TVehicle类,假设其具有VIN(车辆识别号) 如何实现VIN的哈希代码? 更新:在本例中,对象标识不是指“两个对象指针的内存位置标识”,而是指“基于其属性的唯一不变(“不可变”)组合的同一对象的两个实例标识” 因此,我需要的不是通过地图中的存储地址来搜

如果我使用TDictionary在Delphi中实现车主关系,我应该如何实现IEqualityComparer的Equals和GetHashCode函数?(GetHashCode返回一个整数,该整数在TDictionary中用于哈希。)

对于TVehicle类,假设其具有VIN(车辆识别号)

如何实现VIN的哈希代码?

更新:在本例中,对象标识不是指“两个对象指针的内存位置标识”,而是指“基于其属性的唯一不变(“不可变”)组合的同一对象的两个实例标识”

因此,我需要的不是通过地图中的存储地址来搜索车辆,而是具有我正在查找的id的车辆

设想一个包含车主数据的数据库,在应用程序启动时加载到字典中。现在,如果用户在申请表中输入VIN,应用程序如何在字典中找到车辆?如果代码使用
VehicleFactory.CreateVehicleFromDatabase(Edit1.Text)创建新实例
并在字典中搜索此对象,Equals的默认实现将不会在映射中找到任何条目,因为它查找内存地址。要查找车辆,Equals需要比较VIN

所以我必须创建一个自定义的IEqualityComparer。实现Equals是微不足道的。但是GetHashCode呢?对于字符串属性,我不能简单地使用字符串的地址(请参阅中的Berry Kelly:“如果您从两个独立的代码段创建相同的字符串,它们将不会共享相同的备份存储”),因此字符串属性的GetHashCode函数需要一个自定义实现


我还发现了这个问题——有一个例子包含了一个
HashValue('Hello World')

如果可能的话,我会在这个例子中使用KISS原则。如果您的实际钥匙是ID而不是车辆本身,那么为什么不使用
t字典
而不是
t字典
?这样,您就不必担心自定义比较器。

在您被告知您的设计和其他方面有异味后,我将回答您的问题,因为创建一个对象键控词典是有效的,并且可以基于与键的内存地址不同的任何内容对其进行比较:

您可以在创建词典时创建新的比较器

例如:

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
类型
TVehicleOwner=类别(t词典)
结束;
//这里还有其他代码
程序TForm2.按钮1单击(发件人:ToObject);
变量
维豪纳:电视所有者;
开始
VehOwner:=TVehOwner.Create(TEqualityComparer.Construct(
//比较器
函数(常量左、右:TVehicle):布尔值
开始
{进行不区分大小写的比较}
结果:=比较文本(Left.FID,Right.FID)=0;
完,,
//哈舍
函数(常量值:TVehicle):整数
开始
{生成哈希代码。}
结果:=您选择的哈希算法(Value.FID);
(完)
);
//这里有更多代码

也就是说,如果有两个实例代表同一个对象,我认为这是代码中的一个缺陷。对于我来说,如果你的TVehicle内存中有一个ID为“ABC”的TVehicle,那么这应该是该车辆的唯一实例,你必须提供一些方法来为你的所有代码获取相同的实例。这样,您就可以在不编写自定义比较器的情况下使用Dictionary类,但更重要的是,您知道您一直在使用同一个对象,并且您的应用程序状态将与代码中的任何内容保持一致,UI或其他接口。

您似乎被误导,认为Delphi字符串没有默认的哈希代码实现

事实并非如此。当您创建一个以字符串值作为键的
t字典时,将根据字符串的内容计算哈希值。如果
Value
是字符串变量,则代码如下所示:

BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);
我想这回答了你关于字符串散列的部分问题


对其他答案的评论,以及我已经删除的那些,都是关于您正在思考的设计问题的有趣讨论。我仍然怀疑您是否认为正确的解决方案是允许TVehicle实例和VIN之间存在多对一关系

您已确认不能有多个具有相同VIN但数据不同的TVehicle实例。在我看来,实现这一点的最佳方法是确保TVehicle实例和VIN之间存在一对一的关系

这种一对一的关系很容易实现。您需要使TVehicle实例的实例化成为工厂类的私有函数。此factory类包含一个包含现有车辆实例的字典,
t字典
。如果您需要获得车辆,请向工厂索取。它返回位于其词典中的现有词典,或合成新词典


毫无疑问,有很多其他的方法可以达到这个效果,但是我强烈建议你考虑一种方法,它只会产生一个VIN的车辆实例。

然后我需要两个字典,一个用于车辆。这意味着我必须同步这两个词典。而且TDictionary更能自我记录。@mjn既然知道了TVehicle的VIN就可以合成它,为什么需要保留任何TVehicle实例呢?梅森说得很对。如果字典键没有由一个属性组成的id,而是一个复合属性,那么这也不起作用value@David我需要TVehicle实例作为字典中的键,在字典中存储它们并设置所有实例属性(例如,从数据库)。如果我仅使用VIN构造函数实例化TVehicle,则此实例的所有字段都有默认值,但我可以使用此特殊实例使用com来查找字典条目