Delphi 有没有一种方法可以在知道字段名和值的情况下更新记录中的字段

Delphi 有没有一种方法可以在知道字段名和值的情况下更新记录中的字段,delphi,delphi-7,rtti,Delphi,Delphi 7,Rtti,根据记录: MyRecord = record Company: string; Address: string; NumberOfEmplyees: integer; 你能像这样写一个函数调用吗 function UpdateField(var FieldName: string; FieldValue: variant): bool; 以便: UpdateField('Company', 'ABC Co'); 是否将MyRecord.Company更新为“ABC

根据记录:

MyRecord = record
    Company: string;
    Address: string;
    NumberOfEmplyees: integer;
你能像这样写一个函数调用吗

function UpdateField(var FieldName: string; FieldValue: variant): bool;
以便:

UpdateField('Company', 'ABC Co');
是否将MyRecord.Company更新为“ABC公司”

我找了一个例子,但我找到的都是数据库。任何为我指明正确方向的帮助都将不胜感激

谢谢,
Charles

您需要Delphi的现代版本来完成您的要求,而无需手动编写查找代码,例如通过表格


Delphi 2010中引入的更新RTTI可以支持您正在寻找的内容,但Delphi 7中没有任何东西可以对记录执行此操作。

Delphi 7 RTTI所知道的,并且可以从
类型信息(aRecordType)
中检索到的是:

  • 记录类型名称
  • 创纪录的全球规模
  • 记录中每个引用计数变量的偏移量和类型(字符串/变量/宽字符串/动态数组/其他包含引用计数变量的嵌套记录)
最新信息对于在运行时释放记录中每个引用计数变量所使用的内存或复制记录内容是必需的。记录的初始化也可以在编译器生成的代码中执行(如果记录是在堆栈上创建的),或者通过
\u InitializeRecord()
方法执行,或者在实例化类或动态数组时全局填充为0

在所有版本的Delphi中,
record
object
类型都是相同的

您可以注意到有一个(至少包括Delphi2009和2010),它有时不会创建初始化堆栈上对象的代码。您必须改用record,但它会破坏与以前版本的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;
例如,使用这些数据,您可以执行以下操作:

  • 一些,对应于记录(包括a)
  • 一种方法(也在动态数组中,为记录的动态数组提供类似TList的方法,以及更多)
例如,以下是如何使用此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;
但您根本没有实现请求所需的信息:

  • 字段名不存储在RTTI中(仅全局记录类型名,甚至不总是AFAIK)
  • 只有引用计数的字段具有偏移量,而不是其他简单类型的字段(如integer/double…)
如果您确实需要此功能,Delphi 7下的唯一解决方案是使用类而不是记录。


在Delphi 7上,如果您使用已发布字段创建一个类,那么您将拥有所有已发布字段所需的所有信息。然后您可以更新此类已发布字段内容。这是VCL运行时在将.dfm内容取消序列化为类实例或使用.

+1时所做的操作。这是一个非常好且问题明确的问题。但是如果存在ne我的意思是,你可以使用类似于
Name=Value
格式的字符串存储字段,并为此使用TStringList或TIniFile。我缺少什么?@Charles你会接受答案吗?或者你还有其他问题吗?我是害怕。谢谢你的帮助。@mason谢谢你的更正。他们这里有delphi RTTI徽章吗?如果有,你应该有它!ts!
     procedure UpdateStringField(StringFieldIndex: integer; const FieldValue: string);