有没有办法在知道字段名称和值的情况下更新记录中的字段

发布于 2024-11-16 14:57:40 字数 436 浏览 4 评论 0 原文

给定一条记录:

MyRecord = record
    Company: string;
    Address: string;
    NumberOfEmplyees: integer;

您能否编写一个函数调用,

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

如下所示:

UpdateField('Company', 'ABC Co');

将 MyRecord.Company 更新为“ABC Co”?

我寻找了一个示例,但我找到的所有内容都是针对数据库的。任何为我指明正确方向的帮助都将受到赞赏。

谢谢, 查尔斯

Given a Record:

MyRecord = record
    Company: string;
    Address: string;
    NumberOfEmplyees: integer;

can you write a function call like

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

so that:

UpdateField('Company', 'ABC Co');

would update MyRecord.Company to 'ABC Co'?

I looked for an example but everything I found is for a database. Any help pointing me in the right direction is appreciated.

Thanks,
Charles

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

三月梨花 2024-11-23 14:57:40

Delphi 7 RTTI 知道并且可以从 TypeInfo(aRecordType) 检索的是:

  • 记录类型名称;
  • 全球规模创纪录;
  • 记录中每个引用计数变量的偏移量和类型(字符串/变量/宽字符串/动态数组/包含引用计数变量的其他嵌套记录)。

最新信息对于在运行时释放记录内每个引用计数变量使用的内存或复制记录内容是必需的。记录的初始化也可以在编译器生成的代码中执行(如果记录是在堆栈上创建的),可以通过 _InitializeRecord() 方法执行,也可以在类或动态数组被实例化。

在所有版本的 Delphi 中,recordobject 类型都是相同的。

您可以注意到,现代版本的 Delphi 中存在一个错误(包括 Delphi 2009)至少 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;

使用此数据,您可以执行以下操作:

例如,以下是如何使用此 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);

但是您根本没有实现请求所需的信息:

  • 字段名称不存储在其中。 RTTI(仅全局记录类型名称,甚至据我所知并不总是如此);
  • 只有引用计数字段才有偏移量,其他简单类型字段(例如整数/双精度......)没有。

如果您确实需要此功能,Delphi 7 下唯一的解决方案是不使用记录,而是使用类。

在 Delphi 7 上,如果您创建一个包含已发布字段的类,您将拥有以下所有需要的信息:所有已发布的字段。然后您可以更新此类已发布的字段内容。这就是 VCL 运行时将 .dfm 内容反序列化到类实例中或使用 ORM 方法

What Delphi 7 RTTI knows, and can be retrieved from TypeInfo(aRecordType), is:

  • The record type name;
  • The record global size;
  • The offset and type of each reference-counted variable within the record (string/variant/widestring/dynamic array/other nested record containing reference-counted variables).

The latest information is necessary to free the memory used by each reference-counted variables inside the record, or copy the record content, at run-time. The initialization of the record is also performed either in compiler-generated code (if the record is created on the stack), either via _InitializeRecord() method, either with a global fill to 0 when a class or a dynamic array is instanciated.

It's the same for both record and object types, in all version of Delphi.

You can note that there is a bug in modern version of Delphi (including Delphi 2009 and 2010 at least), which sometimes don't create the code for initializing objects on stack. You'll have to use record instead, but it will break compatibility with previous version of Delphi. :(

Here are the structure used for storing this RTTI data:

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;

Using this data, here is for instance what you can do:

For instance, here is how two records of the same type can be compared, using this 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;

So for your purpose, what Delphi 7 RTTI could let you know at runtime, is the position of every string within a record. Using the code above, you could easily create a function using the field index:

     procedure UpdateStringField(StringFieldIndex: integer; const FieldValue: string);

But you simply don't have the needed information to implement your request:

  • The field names are not stored within the RTTI (only the global record type name, and even not always AFAIK);
  • Only reference-counted fields have an offset, not other fields of simple type (like integer/double...).

If you really need this feature, the only solution under Delphi 7 is to use not records, but classes.

On Delphi 7, if you create a class with published fields, you'll have all needed information for all published fields. Then you can update such published field content. This is what the VCL runtime does when unserializing the .dfm content into class instances, or with an ORM approach.

悸初 2024-11-23 14:57:40

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

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

You need modern versions of Delphi to do what you ask for without resorting to manually coding the lookups, e.g. via a table.

The updated RTTI introduced in Delphi 2010 can support what you are looking for, but there's nothing in Delphi 7 that will do this for records.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文