给出记录:
MyRecord = record
Company: string;
Address: string;
NumberOfEmplyees: integer;
你能写一个像
这样的函数调用吗?function UpdateField(var FieldName: string; FieldValue: variant): bool;
这样:
UpdateField('Company', 'ABC Co');
会将MyRecord.Company更新为'ABC Co'吗?
我找了一个例子,但我发现的一切都是为了数据库。任何帮助我指向正确方向的人都表示赞赏。
谢谢, 查尔斯
答案 0 :(得分:12)
Delphi 7 RTTI知道并且可以从TypeInfo(aRecordType)
检索的内容是:
需要最新信息来释放记录中每个引用计数变量使用的内存,或者在运行时复制记录内容。记录的初始化也可以在编译器生成的代码中执行(如果在堆栈上创建记录),可以通过_InitializeRecord()
方法执行,也可以在实例化类或动态数组时将全局填充设置为0
在所有版本的Delphi中,record
和object
类型都是相同的。
您可以注意到有bug in modern version of Delphi(至少包括Delphi 2009和2010),有时不会创建用于在堆栈上初始化对象的代码。您将不得不使用记录,但它将破坏与以前版本的Delphi的兼容性。 :(
以下是用于存储此RTTI数据的结构:
type
TFieldInfo = packed record
TypeInfo: ^PDynArrayTypeInfo; // information of the reference-counted type
Offset: Cardinal; // offset of the reference-counted type in the record
end;
TFieldTable = packed record
Kind: byte;
Name: string[0]; // you should use Name[0] to retrieve offset of Size field
Size: cardinal; // global size of the record = sizeof(aRecord)
Count: integer; // number of reference-counted field info
Fields: array[0..0] of TFieldInfo; // array of reference-counted field info
end;
PFieldTable = ^TFieldTable;
使用此数据,例如,您可以执行以下操作:
例如,下面是使用此RTTI如何比较两个相同类型的记录:
/// check equality of two records by content
// - will handle packed records, with binaries (byte, word, integer...) and
// string types properties
// - will use binary-level comparison: it could fail to match two floating-point
// values because of rounding issues (Currency won't have this problem)
function RecordEquals(const RecA, RecB; TypeInfo: pointer): boolean;
var FieldTable: PFieldTable absolute TypeInfo;
F: integer;
Field: ^TFieldInfo;
Diff: cardinal;
A, B: PAnsiChar;
begin
A := @RecA;
B := @RecB;
if A=B then begin // both nil or same pointer
result := true;
exit;
end;
result := false;
if FieldTable^.Kind<>tkRecord then
exit; // raise Exception.CreateFmt('%s is not a record',[Typ^.Name]);
inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
Field := @FieldTable^.Fields[0];
Diff := 0;
for F := 1 to FieldTable^.Count do begin
Diff := Field^.Offset-Diff;
if Diff<>0 then begin
if not CompareMem(A,B,Diff) then
exit; // binary block not equal
inc(A,Diff);
inc(B,Diff);
end;
case Field^.TypeInfo^^.Kind of
tkLString:
if PAnsiString(A)^<>PAnsiString(B)^ then
exit;
tkWString:
if PWideString(A)^<>PWideString(B)^ then
exit;
{$ifdef UNICODE}
tkUString:
if PUnicodeString(A)^<>PUnicodeString(B)^ then
exit;
{$endif}
else exit; // kind of field not handled
end;
Diff := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
inc(A,Diff);
inc(B,Diff);
inc(Diff,Field^.Offset);
inc(Field);
end;
if CompareMem(A,B,FieldTable.Size-Diff) then
result := true;
end;
因此,出于您的目的,Delphi 7 RTTI可以在运行时通知您,记录中每个字符串的位置。使用上面的代码,您可以使用字段索引轻松创建函数:
procedure UpdateStringField(StringFieldIndex: integer; const FieldValue: string);
但是您根本没有实现您的请求所需的信息:
如果你真的需要这个功能,Delphi 7中唯一的解决方案就是不使用记录,而是使用类。
在Delphi 7上,如果您创建一个包含已发布字段的类,您将获得所有已发布字段的所有必需信息。然后您可以更新此类已发布的字段内容。这是VCL运行时将.dfm内容反序列化为类实例或使用ORM approach时所执行的操作。
答案 1 :(得分:7)
您需要使用现代版本的Delphi来执行您的要求,而无需手动编码查找,例如通过桌子。
Delphi 2010中引入的更新的RTTI可以支持您所寻找的内容,但Delphi 7中没有任何内容可以用于记录。