要解释我的问题,我会在Embarcadero的网站上使用与TDictionary
example非常相似的内容,但我的课程会实现IComparable
。
在Delphi(XE7)中:
TCity = class(TInterfacedObject, IComparable)
Country: String;
Latitude: Double;
Longitude: Double;
function CompareTo(Obj: TObject): Integer;
end;
我希望TDictionary
以城市作为键:
Dictionary := TDictionary<TCity, string>.Create;
CityA := TCity.Create;
CityA.Country := 'United Kingdom';
CityA.Latitude := 51.5;
CityA.Longitude := -0.17;
Dictionary.Add(CityA, 'London');
现在,如果我使用与CityA.CompareTo(CityB)
返回0
之前相同的值创建新城市,例如:
CityB := TCity.Create;
CityB.Country := 'United Kingdom';
CityB.Latitude := 51.5;
CityB.Longitude := -0.17;
在将CityB
添加到词典之前,我期待:
Dictionary.ContainsKey(CityB)
将使用我的CompareTo
实施,ContainsKey
将返回True
,但事实并非如此。我的班级似乎还需要继承TEqualityComparer
。
那么如何将ContainsKey
使用的相等比较器添加到我的班级,而CompareTo
已经存在?
我知道我可以使用TDelegatedEqualityComparer
和匿名方法来创建在创建字典时使用的自定义比较器。类似的东西:
Dictionary := TDictionary<TCity, string>.Create(CityComparer)
但有没有办法在TCity
中包含相等比较器,这样在创建字典时我不需要一个?
答案 0 :(得分:7)
你在这里混合两件事。 IComparable
用于排序。
IEqualityComparer<T>
用于检查两个项是否相等,但它还具有字典在内部使用的GetHashCode
方法。
对于对象,IEqualityComparer<T>
的默认实现使用TObject中的虚拟GetHashCode
和Equals
方法。因此,如果您想将两个不同的实例视为相等,则有两个选项:
在您的班级
提供IEqualityComparer<T>
答案 1 :(得分:1)
据我所知。如果你没有提供IEqualityComparer,那么构造函数TDictionary将使用默认的密钥。
查看构造函数:
constructor TDictionary<TKey,TValue>.Create(ACapacity: Integer; const AComparer: IEqualityComparer<TKey>);
var
cap: Integer;
begin
inherited Create;
if ACapacity < 0 then
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
FComparer := AComparer;
if FComparer = nil then
FComparer := TEqualityComparer<TKey>.Default; // <-- HERE!
SetCapacity(ACapacity);
end;
她在GetBucketIndex
使用
function TDictionary<TKey,TValue>.GetBucketIndex(const Key: TKey; HashCode: Integer): Integer;
begin
...
// Found: return location.
if (hc = HashCode) and FComparer.Equals(FItems[Result].Key, Key) then
...
end;
所以不,你必须在构造函数中提供IComparable