- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
当我使用 TObjectDictionary(其中 TKey 是对象)时,我的应用程序无法正常工作。我有两个单元,包含两个类。第一单元:
unit RubTerm;
interface
type
TRubTerm = Class(TObject)
private
FRubricName: String;
FTermName: String;
public
property RubricName: String read FRubricName;
property TermName: String read FTermName;
constructor Create(ARubricName, ATermName: String);
end;
implementation
constructor TRubTerm.Create(ARubricName, ATermName: String);
begin
Self.FRubricName := ARubricName;
Self.FTermName := ATermName;
end;
end;
第二个单元:
unit ClassificationMatrix;
interface
uses
System.Generics.Collections, System.Generics.Defaults, System.SysUtils, RubTerm;
type
TClassificationMatrix = class(TObject)
private
FTable: TObjectDictionary<TRubTerm, Integer>;
public
constructor Create;
procedure TClassificationMatrix.AddCount(ADocsCount: Integer; ARubName, ATermName: String);
function TClassificationMatrix.GetCount(ARubName, ATermName: String): Integer;
end;
implementation
constructor TClassificationMatrix.Create;
begin
FTable := TObjectDictionary<TRubTerm, Integer>.Create;
end;
procedure TClassificationMatrix.AddCount(ADocsCount: Integer; ARubName, ATermName: String);
var
ARubTerm: TRubTerm;
begin
ARubTerm := TRubTerm.Create(ARubName, ATermName);
FTable.Add(ARubTerm, ADocsCount);
end;
function TClassificationMatrix.GetCount(ARubName, ATermName: String): Integer;
var
ARubTerm: TRubTerm;
begin
ARubTerm := TRubTerm.Create(ARubName, ATermName);
FTable.TryGetValue(ARubTerm, Result);
end;
end;
但是这段代码工作不正常:
procedure TestTClassificationMatrix.TestGetCount;
var
DocsCountTest: Integer;
begin
FClassificationMatrix.AddCount(10, 'R', 'T');
DocsCountTest := FClassificationMatrix.GetCount('R', 'T');
end;
// DocsCountTest = 0! Why not 10? Where is problem?
谢谢!
最佳答案
这里的根本问题是您的类型的默认相等比较器的行为与您希望的方式不同。您希望相等意味着值相等,但默认比较给出引用相等。
您希望值相等的事实强烈表明您应该使用值类型而不是引用类型。这是我建议的第一个改变。
type
TRubTerm = record
RubricName: string;
TermName: string;
class function New(const RubricName, TermName: string): TRubTerm; static;
class operator Equal(const A, B: TRubTerm): Boolean;
class operator NotEqual(const A, B: TRubTerm): Boolean;
end;
class function TRubTerm.New(const RubricName, TermName: string): TRubTerm;
begin
Result.RubricName := RubricName;
Result.TermName := TermName;
end;
class operator TRubTerm.Equal(const A, B: TRubTerm): Boolean;
begin
Result := (A.RubricName=B.RubricName) and (A.TermName=B.TermName);
end;
class operator TRubTerm.NotEqual(const A, B: TRubTerm): Boolean;
begin
Result := not (A=B);
end;
我已添加TRubTerm.New
作为辅助方法,可以轻松初始化记录的新实例。为了方便起见,您可能还会发现重载相等和不等运算符很有用,就像我上面所做的那样。
切换到值类型后,您还需要更改字典以进行匹配。使用TDictionary<TRubTerm, Integer>
而不是TObjectDictionary<TRubTerm, Integer>
。切换到值类型还可以修复现有代码中的所有内存泄漏。您现有的代码创建对象但从不销毁它们。
这已经让你回家了,但你仍然需要为你的字典定义一个相等比较器。记录的默认比较器将基于引用相等,因为字符串尽管表现为值类型,但仍存储为引用。
要制作合适的相等比较器,您需要实现以下比较函数,其中 T
替换为 TRubTerm
:
TEqualityComparison<T> = reference to function(const Left, Right: T): Boolean;
THasher<T> = reference to function(const Value: T): Integer;
我将它们实现为记录的静态类方法。
type
TRubTerm = record
RubricName: string;
TermName: string;
class function New(const RubricName, TermName: string): TRubTerm; static;
class function EqualityComparison(const Left,
Right: TRubTerm): Boolean; static;
class function Hasher(const Value: TRubTerm): Integer; static;
class operator Equal(const A, B: TRubTerm): Boolean;
class operator NotEqual(const A, B: TRubTerm): Boolean;
end;
实现EqualityComparison
很简单:
class function TRubTerm.EqualityComparison(const Left, Right: TRubTerm): Boolean;
begin
Result := Left=Right;
end;
但是哈希器需要更多的思考。您需要单独对每个字段进行散列,然后组合散列。供引用:
代码如下所示:
{$IFOPT Q+}
{$DEFINE OverflowChecksEnabled}
{$Q-}
{$ENDIF}
function CombinedHash(const Values: array of Integer): Integer;
var
Value: Integer;
begin
Result := 17;
for Value in Values do begin
Result := Result*37 + Value;
end;
end;
{$IFDEF OverflowChecksEnabled}
{$Q+}
{$ENDIF}
function GetHashCodeString(const Value: string): Integer;
begin
Result := BobJenkinsHash(PChar(Value)^, SizeOf(Char) * Length(Value), 0);
end;
class function TRubTerm.Hasher(const Value: TRubTerm): Integer;
begin
Result := CombinedHash([GetHashCodeString(Value.RubricName),
GetHashCodeString(Value.TermName)]);
end;
最后,当你实例化你的字典时,你需要提供一个 IEqualityComparison<TRubTerm>
。像这样实例化你的字典:
Dict := TDictionary<TRubTerm,Integer>.Create(
TEqualityComparer<TRubTerm>.Construct(
TRubTerm.EqualityComparison,
TRubTerm.Hasher
)
);
关于delphi - 使用对象作为 TObjectDictionary 中的键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18068977/
这个问题已经有答案了: Use objects as keys in TObjectDictionary (2 个回答) 已关闭 7 年前。 我有以下class : TTest = class pri
Delphi XE6 - 我正在使用 TObjectDictionary 和自定义类。我创建、添加,然后免费。只要我这样做,一切就都很好。如果我执行“TryGetValue”,我会收到“无效指针操作”
当我使用 TObjectDictionary(其中 TKey 是对象)时,我的应用程序无法正常工作。我有两个单元,包含两个类。第一单元: unit RubTerm; interface type
分享这个问题的代码作为引用:Delphi TPair Exception 如何在不使用 TPair 且不从列表中提取/移除/删除该对的情况下从 TObjectDictionary 具体条目中检索键和值
看看这段代码: dic:=TObjectDictionary.Create([doOwnsValues]); testObject:=TObject.Create; dic.AddOrSetValue
Delphi XE2 在线帮助(以及 Embarcadero DocWiki)关于 TObjectDictionary 的文档非常薄弱(或者我太笨了,找不到它)。 据我了解,它可用于存储可通过字符串键
我正在尝试使用 Delphi 2010 的 TObjectDictionary 泛型。 我想传递该泛型类的 Values 属性的枚举器,但编译器似乎不想让我...示例: TAttributeSta
我可以更改 TDictionary 中的键而不更改值吗? 为了解释一下,我使用的是 TObjectDictionary,它派生自 Delphi XE Generics.Collections 单元中的
我是一名优秀的程序员,十分优秀!